使用多线程可以利用多核CPU的计算能力,可以提供更好的程序响应能力,但是每个线程都有开销,需要注意控制线程的数量。
1. System.Threading.Thread
使用多线程最直接的是使用System.Threading.Thread。回调函数可以接受一个参数、或者不接受参数,没有返回值。
Thread t = new Thread(Echo);
t.Start("test");
t.Join();
t = new Thread(DoSomeThing);
t.Start();
private static void Echo(object obj)
{
Console.WriteLine(obj);
Thread.Sleep(100);
}
private static void DoSomeThing()
{
Console.WriteLine("DoSomeThing, threadid={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
}
2. 线程池
创建和销毁线程需要大量的时间,太多的线程还会消耗大量的内存。使用线程池可以重用线程,方便线程的管理。每个CLR有一个线程池,里面的线程分为Worker和IO线程。
回调函数只能是接受一个参数的函数,没有返回值。
ThreadPool.QueueUserWorkItem(Echo, "test2");
3. 取消
使用CancellationTokenSource和对应的CancellationToken实现协作式取消,CancellationToken是结构类型,包含对CancellationTokenSource的引用。CancellationTokenSource封装了数据,CancellationToken封装了操作。每个线程都有自己的Token,但是访问同一个Source,通过改变Source的状态,可以通知所有监测Token的线程。
CancellationTokenSource cts1 = new CancellationTokenSource();
cts1.Token.Register(
() =>
{
Console.WriteLine("cts1 canceled, threadid={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
});
CancellationTokenSource cts2 = new CancellationTokenSource();
cts2.Token.Register(
() =>
{
Console.WriteLine("cts2 canceled, threadid={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
});
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);
linkedCts.Token.Register(
() =>
{
Console.WriteLine("linkedCts cancled, threadid={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
});
Task.Factory.StartNew(
() =>
{
Thread.Sleep(100);
if (cts1.Token.IsCancellationRequested)
{
Console.WriteLine("Canceled. threadid={0}", Thread.CurrentThread.ManagedThreadId);
}
});
cts1.Cancel();
4. Task
线程池的任务没有办法知道状态,也没有返回值。Task建立在线程池基础上,提供了这些功能。
var t = new Task<int>(sum, 20);
t.Start();
Console.WriteLine(t.Result);
Task用法的一个例子。包括TaskFactory,Cancel,ContinueWith
Task parent = new Task(
() =>
{
CancellationTokenSource cts = new CancellationTokenSource();
TaskFactory<int> tf = new TaskFactory<int>(
cts.Token,
TaskCreationOptions.AttachedToParent,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
var childTasks = new[]
{
tf.StartNew(() => sum(cts.Token, 1000)),
tf.StartNew(() => sum(cts.Token, 2000)),
tf.StartNew(() => sum(cts.Token, int.MaxValue))
};
for (int i = 0; i < childTasks.Length; i++)
{
childTasks[i].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
}
tf.ContinueWhenAll(
childTasks,
completedTasks =>
completedTasks.Where(t => !t.IsFaulted && !t.IsCanceled).Max(t => t.Result),
CancellationToken.None)
.ContinueWith(
t => Console.WriteLine("The max is {0}", t.Result),
TaskContinuationOptions.ExecuteSynchronously);
});
parent.ContinueWith(
p =>
{
StringBuilder sb =
new StringBuilder(
string.Format("Following exception(s) occurred. {0}", Environment.NewLine));
foreach (var e in p.Exception.Flatten().InnerExceptions)
{
sb.AppendLine(string.Format(" {0}: {1}", e.GetType(), e.Message));
}
Console.WriteLine(sb.ToString());
},
TaskContinuationOptions.OnlyOnFaulted);
parent.Start();