goo*_*ate 12 .net c# multithreading .net-4.0 .net-4.5
委托是一些使.NET 引用中的线程更容易的对象.它们可用于异步调用方法.框架4.5(或更早版本)中存在哪些其他对象使线程的使用更容易或更不容易出错?
什么是其他抽象使并发和多线程更容易?
注意:这个问题将更新此.
Bri*_*eon 21
我倾向于回答很多与多线程有关的问题,我经常看到以不同方式提出的相同基本问题.我将介绍我多年来看到的最常见的问题,并解释新技术如何使这些问题更容易解决.
关闭循环变量
这不是特定于线程的问题,但使用线程肯定会放大问题.C#5.0 foreach
通过为每次迭代创建一个新变量来解决循环的这个问题.您将不再需要为lambda表达式闭包创建特殊变量.不幸的是,for
循环仍然需要使用特殊的捕获变量来处理.
等待异步任务完成
.NET 4.0引入了这个CountdownEvent
类,它封装了许多等待完成许多任务所需的逻辑.大多数初级开发人员使用Thread.Join
电话或单个WaitHandle.WaitAll
电话.这两者都存在可扩展性问题.旧模式是使用单个模式ManualResetEvent
并在计数器达到零时发出信号.计数器已使用Interlocked
该类更新.CountdownEvent
使这种模式更容易.只要记住将你的主人当作工人对待,以避免如果一名工人在所有工人排队之前完成就会发生微妙的竞争状况.
.NET 4.0还引入了Task
可以通过其链接子任务的类TaskCreationOptions.AttachedToParent
.如果您呼叫Task.Wait
父母,它也会等待所有子任务完成.
生产者 - 消费者
.NET 4.0引入了BlockingCollection
类似普通队列的类,除了它可以在集合为空时阻塞.您可以通过调用来调用Add
和出列对象来对对象进行排队Take
.Take
阻止直到某个项目可用.这大大简化了生产者 - 消费者逻辑.曾经是开发人员试图编写自己的阻塞队列类的情况.但是,如果你不知道自己在做什么,那么你真的可以搞砸了......糟糕.实际上,微软在MSDN文档中有一个阻塞队列示例的时间最长,这本身就是严重破坏的.幸运的是,它已被删除.
使用工作线程进度更新UI
BackgroundWorker
对于新手开发人员来说,从WinForm应用程序中脱离后台任务的介绍更容易.主要好处是您可以ReportProgress
在DoWork
事件处理程序中调用,并且ProgressChanged
事件处理程序将自动封送到UI线程中.当然,任何跟踪我在SO上的答案的人都知道我对编组操作(通过Invoke
等)的感觉,作为用简单的进度信息更新UI的解决方案.我总是扯掉它,因为它通常是一种可怕的方法.BackgroundWorker
仍然迫使开发人员进入推送模型(通过后台编组操作),但至少它在幕后完成了所有这些工作.
Invoke的不雅
我们都知道只能从UI线程访问UI元素.这通常意味着开发者必须通过使用编组作业ISynchronizeInvoke
,DispatcherObject
或SynchronizationContext
以控制转移回UI线程.但是让我们面对现实.这些编组操作看起来很丑陋.Task.ContinueWith
使这更优雅,但真正的荣耀await
成为C#5的新异步编程模型的一部分.await
可以用来等待a Task
以这样的方式完成,即在任务运行时临时中断流控制,然后在右同步上下文中的那个位置返回.没有什么比await
用作所有这些Invoke
电话的替代品更优雅和令人满意.
并行编程
我经常看到问题是如何并行发生的.旧的方法是创建一些线程或使用ThreadPool
..NET 4.0使用了TPL和PLINQ.该Parallel
班是一个伟大的方式来获得一个回路并联去的迭代.AsParallel
对于普通的旧LINQ ,PLINQ 是同一枚硬币的另一面.这些新的TPL功能大大简化了这类多线程编程.
.NET 4.5引入了TPL数据流库.它旨在使复杂的并行编程问题变得优雅.它将类抽象为块.它们可以是目标块或源块.数据可以从一个块流到另一个块.有许多不同的模块,包括BufferBlock<T>
,BroadcastBlock<T>
,ActionBlock<T>
等所有做不同的事情.当然,整个库将进行优化,以便与新的async
和await
关键字一起使用.这是一组激动人心的新课程,我认为这些课程会慢慢流行起来.
优雅的终止
你怎么得到一个线程停止?我经常看到这个问题.最简单的方法是打电话Thread.Abort
,但我们都知道这样做的危险...我希望.有许多不同的方法可以安全地完成这项工作..NET 4.0引入了一个更统一的概念,称为取消通过CancellationToken
和CancellationTokenSource
.后台任务可以轮询IsCancellationRequested
或只是ThrowIfCancellationRequested
在安全点调用,以优雅地中断他们正在做的任何工作.其他线程可以调用Cancel
请求取消.
好吧,让我们看看这里:
ThreadPool
类 - 有点旧,但对于简单的生产者 - 消费者模式仍然可靠.BackgoundWorker
(.NET 2.0+) - 另一种老式构造,为在GUI应用程序中在后台执行任务提供有用的功能.Timer
s - 使用后台线程以指定的时间间隔执行代码很有用.Task
类(.NET 4.0 +) -线程是在底层的线程池中运行,并提供了许多有用的功能,如异常编组和调度抽象.对于所谓的"任务并行"模式很有用.Parallel.For
,Parallel.ForEach
(.NET 4.0+) - 适合在一组数据上并行执行相同的操作.对于所谓的"数据并行"模式很有用.Parallel.Invoke
(.NET 4.0+) - 对Task
s 的进一步抽象.简单地并行激活几段代码(方法,lambda).