委托(delegate)是一些使 .NET 中的线程更容易的对象 reference .它们可用于异步调用方法。框架 4.5(或更早版本)中还有哪些其他对象可以使线程的使用更容易或更不容易出错?
还有哪些抽象使并发和多线程更容易?
注意:此问题更新 this .
最佳答案
我倾向于回答很多与多线程相关的问题,而且我经常看到以各种不同方式提出的相同基本问题。我将介绍多年来我所看到的最常见的问题,并解释新技术如何使解决这些问题变得更容易。
结束循环变量
这不是线程特有的问题,但线程的使用肯定会放大问题。 C# 5.0 为 foreach
修复了这个问题通过为每次迭代创建一个新变量来循环。您将不再需要为 lambda 表达式闭包创建特殊变量。不幸的是,for
循环仍然需要使用特殊的捕获变量来处理。
等待异步任务完成
.NET 4.0 引入了 CountdownEvent
类里面封装了很多等待很多任务完成所需的逻辑。大多数初级开发人员使用 Thread.Join
来电或单WaitHandle.WaitAll
打电话。这两者都有可扩展性问题。旧模式是使用单个 ManualResetEvent
并在计数器达到零时发出信号。计数器已使用 Interlocked
更新类。 CountdownEvent
使这种模式更容易。请记住,将您的 main 也视为一个 worker ,以避免在所有 worker 排队之前一个 worker 完成时可能发生的微妙竞争条件。
.NET 4.0 还引入了 Task
可以通过 TaskCreationOptions.AttachedToParent
链接子任务的类.如果您拨打 Task.Wait
在父级上,它也会等待所有子任务完成。
生产者-消费者
.NET 4.0 引入了 BlockingCollection
类的行为类似于普通队列,但它可以在集合为空时阻塞。您可以通过调用 Add
将对象排队。并通过调用 Take
使对象出列. Take
阻塞直到一个项目可用。这大大简化了生产者-消费者逻辑。过去,开发人员试图编写自己的阻塞队列类。但是,如果你不知道你在做什么,那么你真的可以把它搞砸......糟糕。事实上,长期以来,微软在 MSDN 文档中有一个阻塞队列示例,它本身就被严重破坏了。幸运的是,它已被删除。
使用工作线程进度更新 UI
简介BackgroundWorker
使新手开发人员更容易从 WinForm 应用程序中分离后台任务。主要好处是可以拨打ReportProgress
来自 DoWork
事件处理程序和 ProgressChanged
事件处理程序将自动编码到 UI 线程上。当然,任何在 SO 上跟踪我的答案的人都知道我对编码(marshal)操作(通过 Invoke
等)作为使用简单进度信息更新 UI 的解决方案的感受。我一直在抨击它,因为它通常是一种可怕的方法。 BackgroundWorker
仍然迫使开发人员进入推送模型(通过后台的编码操作),但至少它在幕后完成了所有这些。
Invoke 的不雅
我们都知道一个 UI 元素只能从 UI 线程访问。这通常意味着开发人员必须通过 ISynchronizeInvoke
使用编码(marshal)操作。 , DispatcherObject
, 或 SynchronizationContext
将控制权转移回 UI 线程。但让我们面对现实吧。这些编码操作看起来很难看。 Task.ContinueWith
让它更优雅一点,但真正的荣耀归于 await
作为 C# 5 新异步编程模型的一部分。 await
可用于等待 Task
以这样一种方式完成,即在任务运行时暂时中断流控制,然后在正确的同步上下文中的那个位置返回。没有什么比使用 await
更优雅和令人满意的了作为所有这些的替代品 Invoke
调用。
并行编程
我经常看到一些问题,询问事情如何并行发生。旧的方法是创建几个线程或使用 ThreadPool
. .NET 4.0 使用了 TPL 和 PLINQ。 Parallel
class 是让循环的迭代并行进行的好方法。和 PLINQ 的 AsParallel
对于普通的旧 LINQ 来说,这是同一枚硬币的另一面。这些新的 TPL 特性极大地简化了此类多线程编程。
.NET 4.5 引入了 TPL 数据流库。它旨在使原本复杂的并行编程问题变得优雅。它将类抽象为块。它们可以是目标块或源块。数据可以从一个块流到另一个块。有许多不同的块,包括 BufferBlock<T>
, BroadcastBlock<T>
, ActionBlock<T>
,等等,都做不同的事情。当然,整个库将针对新的 async
进行优化。和 await
关键词。这是一组令人兴奋的新类(class),我认为它会慢慢流行起来。
优雅终止
你如何让线程停止?我经常看到这个问题。最简单的方法是拨打Thread.Abort
,但我们都知道这样做的危险......我希望。有许多不同的方法可以安全地做到这一点。 .NET 4.0 通过 CancellationToken
引入了一个更统一的概念,称为取消。和 CancellationTokenSource
.后台任务可以轮询IsCancellationRequested
或拨打 ThrowIfCancellationRequested
在安全的地方优雅地中断他们正在做的任何工作。其他线程可以拨打Cancel
要求取消。
关于c# - 哪些 .NET 4.5(或更早版本)更高级别的构造使线程更容易?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10533971/