去年底工作上遇到一个奇葩的需求,之前写好的代码实时读取数据库,并做出一些操作;结果领导认为这样不好,要改成先去数据库把该做的都做了,结果存在数据库中,用的时候直接读数据库。试了一下,数据库数据量太多,顺序执行一遍需要4小时,不满足时间上的要求,于是只能改一改,改成多线程的了。
因为之前的程序是完整的,所以直接抠出来,加了个多线程的壳,凑合完成了领导要求。因为这种多线程不涉及线程间通讯,比较简单。下面给出源代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Configuration;
using System.IO;
namespace MultiTask
{
class Program
{
static int m_timeout;
static void Main(string[] args)
{
// 从配置文件中读入并发线程数和单个线程执行超时时间
int concurrency = Convert.ToInt32(ConfigurationManager.AppSettings["concurrency"]);
m_timeout = Convert.ToInt32(ConfigurationManager.AppSettings["timeout"]);
// 创建线程任务排队,concurrency即为同时运行的线程数量
LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(concurrency);
List<Task> tasks = new List<Task>();
// Create a TaskFactory and pass it our custom scheduler.
TaskFactory factory = new TaskFactory(lcts);
CancellationTokenSource cts = new CancellationTokenSource();
var m_list = Init(); // 把数据从数据库中取出来,放在m_list中准备处理
// 创建任务,并添加到任务工厂运行
foreach (var ele in m_list)
{
Info para = new Info(ele); // 将数据塞到合适的数据结构中
Task t1 = factory.StartNew(() => { TestRun(para); }, cts.Token);
tasks.Add(t1);
}
// 等待任务完成
Task.WaitAll(tasks.ToArray());
cts.Dispose();
Console.WriteLine("Successful completion.");
KillProcess();
//Console.ReadKey();
}
// 把数据从数据库中取出来
static List<string> Init()
{
...
...
}
// 万一有卡死的进程,杀掉
static void KillProcess()
{
foreach (Process badprocess in Process.GetProcessesByName("TestRun"))
{
badprocess.Kill();
}
}
static void TestRun(Info info)
{
Process p = new Process();
p.StartInfo.FileName = "TestRun.exe"; // 运行以前的老程序
p.StartInfo.Arguments = info.ID + " " + info.PRICE;
p.StartInfo.WorkingDirectory = Environment.CurrentDirectory;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.Start();
bool flag = p.WaitForExit(m_timeout); // 等待运行结束或超时
if (!flag) // 如果任务超时,打印log
{
FileStream fs = new FileStream("record.log", FileMode.Append);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("[time out]: id = {0}, price = {1}", info.ID, info.PRICE);
sw.Flush();
sw.Close();
fs.Close();
}
}
}
// 数据类
public class Info
{
public int ID;
public double PRICE;
...
...
}
}
上面的程序运行后,会同时运行concurrency个线程。当其中一个线程运行完或超时结束后,就再运行一个新的线程,直到所有任务全部完成。就实现领导提的简单需求来说,已经足够了。