c# - 哪些 .NET 4.5(或更早版本)更高级别的构造使线程更容易?

标签 c# .net multithreading .net-4.0 .net-4.5

委托(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/

相关文章:

multithreading - 在异步可插拔协议(protocol)线程中 - 如何将其关联/解析到实现::SetSite() 的特定 COM 对象?

android - 在这种情况下如何避免 IllegalStateException : The content of the adapter has changed but ListView did not receive a notification

c# - C# 中的线程和 GUI 元素

c# - 如何将 azure blob 存储文件作为 FileStreamResult 返回?

c# - 将 SQL 数据读取器数据加载到 DataTable 时遇到问题

.net - 泛型 hell 或在 .NET 中进行程序集级别类型参数化(程序集范围泛型)需要什么

.net - Tuple<int, int> 与 int[2] 内存使用情况

c# - ASP.NET MVC 3 中的更新面板

c# - 如果由于程序退出/主窗体关闭而终止,则窗体无法正常关闭

c# - 将 Azure Key Vault key 传递给 Blazor 应用程序中的 Services.Configure