异步编程
C# 里多线程编程写法有很多种,这里记录三种。
1. Thread
Thread 算是比较常用的写法,出来时间也比较早,一般分带参数和不带参数两种写法:
Thread thread1 = new Thread(new ThreadStart(ThreadMethod1));//不带参数
thread1.Start();
Thread thread2 = new Thread(new ParameterizedThreadStart(ThreadMethod2));//带参数
thread2.Start(para);
某些时候,不能任由线程各自运行,比如需要等待所有线程结束后再去做某件事,这个时候就需要线程同步,同步方式有很多种,并不是本文重点,感兴趣的请自行百度。这里介绍一种事件等待方式,而这种方式就可以用到带参数的线程写法:
static void Main(string[] args)
{
TestThread();
}
private static void TestThread()
{
ManualResetEvent resetEvent = new ManualResetEvent(false);
Thread thread1 = new Thread(new ParameterizedThreadStart(StartByThread));
thread1.Start(resetEvent);
WaitThread(resetEvent);
}
private static void StartByThread(object para)
{
ManualResetEvent mre= (ManualResetEvent)para;
for (int i = 1; i < 5; i++)
{
Console.WriteLine("Execute "+i.ToString());
Thread.Sleep(1000);
}
mre.Set();
}
private static void WaitThread(ManualResetEvent e)
{
var waits = new List<EventWaitHandle>();
waits.Add(e);
WaitHandle.WaitAll(waits.ToArray());
Console.WriteLine("After Wait");
}
2. Task
Task 是 .net 4.0 才有的,严格的说,Task 不应该放在这里和 Thread 相提并论,因为它们压根不一样,什么意思?简单的说:
- Thread仅仅是一条线程,所有操作都是这个 Thread 完成的;
- Task 是将多个操作封装成一个概念上原子操作,但这个操作由哪个 Thread 甚至多个 Thread 来处理并不清楚
Task 应该和 Thread pool 比较合适,因为他们是处理一类任务的。在thread pool时期,我们不能知道一个workitem是否完成,也不能在完成后知道workitem所得出的返回值,task就是封装后解决这个问题的。当然这个只是小方面。Task还优化了thread pool的调用机制,在多核的情况下可以得到更好的效率。
下面介绍 Task 写法:
Task.Factory.StartNew(() => { });//方式1
Task task = new Task(()=> { });//方式2
task.Start();
Task.Run(() => {}); //方式3
当然还有很多种重载写法,具体请自行查阅。
同样,Task 也有等待方法 :
static void Main(string[] args)
{
TestTask();
}
private static void TestTask()
{
Task task1 = new Task(() => {
Console.WriteLine("Task1 executed");
Thread.Sleep(1000);
});
Task task2 = new Task(() => {
Console.WriteLine("Task2 executed");
Thread.Sleep(2000);
});
task1.Start();
task2.Start();
Wait(task1, task2);
}
private static void Wait(params Task[] tasks)
{
Task.WaitAll(tasks);
Console.WriteLine("Wait End");
}
3. async + await
async 和 await 是 .net 4.5才有的,它提供了更简洁的异步编程写法:
static void Main(string[] args)
{
TestAync();
Console.WriteLine("Test End");
Thread.Sleep(2000);
}
private static async void TestAync()
{
await Task.Run(()=> {
Console.WriteLine("Task Run 1");
Thread.Sleep(1000);
Console.WriteLine("Task Run 2");
});
}
async 方法中,也可以等待其它 Task , 但是不能用WaitAll, 只能用WhenAll, 否则可能会卡住当前线程,比如:
static void Main(string[] args)
{
TestAync();
Thread.Sleep(3000);
}
private static async void TestAync()
{
Task task1=new Task(()=> {
Console.WriteLine("Task Run 1");
Thread.Sleep(1000);
});
Task task2 = new Task(() => {
Console.WriteLine("Task Run 2");
Thread.Sleep(2000);
});
task1.Start();
task2.Start();
await Task.WhenAll(task1, task2);
Console.WriteLine("After when all");
}
并行编程
异步编程中,线程之间只要互不影响,考虑同步问题即可。而在并行编程中,则要求多个线程在同一时刻同时运行。必须要有多核cpu。
Parallel
C#中提供了 Parallel 相关方式来支持并行编程, 举个例子:
static void Main(string[] args)
{
Parallel.For(0, 5, (i) => {
Console.WriteLine("Step "+i.ToString());
});
}