请稍侯

多线程相关概念汇总

我会继续翻译一些 .NET 上多线程相关的文章,不过由于原作者风格不同,术语运用方面也有差异,所以我先在这里汇总一下多线程方面相关的概念,以让读者能够有更一致的理解。(一致性也是编程过程中相当重要的,尤其是团队开发过程中,对概念一致的理解可以提高开发效率)

另一方面,这里也是我多线程相关知识体系的整理。

这篇文章就慢慢更新吧,先做个框架,想到什么再加进来。如果读者们有感觉不容易理解的概念,也可以在下面留言提出。

继续阅读...

跑题的字符串国际化简易工具

有时候需要为程序提供多语言支持,然而 .NET 自带的方案有点不便于使用。下面介绍两个简易的工具来让这个工作更轻松些,主要针对字符串资源。

字符串获取

问题

我们知道,.NET 上多语言支持的标准方式是使用 .resx 资源文件,默认语言的资源编译后嵌入主程序集,其它语言的资源文件编译成卫星程序集(satellite assembly)。

Visual Studio 自带的工具可以把对资源的使用生成代码,比如Resources.resx文件就会生成Resources.Designer.cs,其中包含Resources类。资源可以通过这个类上的属性获取。大部分情况下还是挺方便的,但是对于有{0}{1}这种占位符的字符串就很麻烦。每次都要自己string.Format(...)下,尤其还得先确定下里面具体是几个占位符,这是个让人恼火的事情。

方案

某日,在 GitHub 上乱逛,发现了做 EF 7 和 ASP.NET 5 的人搞了个小工具处理这个事情:https://github.com/aspnet/EntityFramework/blob/master/tools/Resources.tt

继续阅读...

高精度定时器实现

背景

.NET Framework 提供了四种定时器,然而其精度都不高(一般情况下 15ms 左右),难以满足一些场景下的需求。

在进行媒体播放、绘制动画、性能分析以及和硬件交互时,可能需要 10ms 以下精度的定时器。这里不讨论这种需求是否合理,它是确实存在的问题,也有相当多的地方在讨论,说明这是一个切实的需求。然而,实现它并不是一件轻松的事情。

这里并不涉及内核驱动层面的定时器,只分析在 .NET 托管环境下应用层面的高精度定时器实现。

Windows 不是实时操作系统,所以任何方案都无法绝对保证定时器的精度,只是能尽量减少误差。所以,系统的稳定性不能完全依赖于定时器,必须考虑失去同步时的处理。

继续阅读...

C#中的多线程 - 并行编程

原文:http://www.albahari.com/threading/part5.aspx

专题:C#中的多线程

并行编程

在这一部分,我们讨论 Framework 4.0 加入的多线程 API,它们可以充分利用多核处理器。

这些 API 可以统称为 PFX(Parallel Framework,并行框架)。Parallel类与任务并行构造一起被称为 TPL(Task Parallel Library,任务并行库)。

Framework 4.0 也增加了一些更底层的线程构造,它们针对传统的多线程。我们之前讲过的:

在继续阅读前,你需要了解第 1 部分 - 第 4 部分中的基本原理,特别是线程安全

继续阅读...

C#中的多线程 - 高级多线程

原文:http://www.albahari.com/threading/part4.aspx

专题:C#中的多线程

非阻塞同步

之前,我们描述了即使是很简单的赋值或更新一个字段也需要同步。尽管总能满足这个需求,一个存在竞争的锁意味着肯定有线程会被阻塞,就会导致由上下文切换和调度的延迟带来的开销,在高并发以及对性能要求很高的场景,这不符合需要。.NET Framework 的 非阻塞(nonblocking)同步构造能够在没有阻塞、暂停或等待的情况下完成简单的操作。

正确编写无阻塞或无锁的多线程代码是棘手的!特别是内存屏障容易用错(volatile 关键字更容易用错)。在放弃使用传统锁之前,请仔细思考是否真的需要非阻塞同步带来的性能优化。切记获得和释放一个无竞争的锁在一个 2010 时代的计算机上仅仅需要 20ns 而已。

无阻塞的方式也可以跨进程工作。一个例子就是它可以被用来读写进程间共享内存。

继续阅读...

C#中的多线程 - 多线程的使用

原文:http://www.albahari.com/threading/part3.aspx

专题:C#中的多线程

基于事件的异步模式

基于事件的异步模式(event-based asynchronous pattern,EAP)提供了一种简单的方式,让类可以提供多线程的能力,而不需要使用者显式启动和管理线程。它也提供如下的功能:

EAP 仅仅是一个模式,所以这些功能需要开发者自己实现。Framework 中仅有少数类采用这个模式,其中最常见的是 BackgroundWorker(接下来就会讲到),以及命名空间System.Net中的WebClient。这个模式本质上就是:类提供一组成员,用于在内部管理多线程,类似于下边的代码:

// 这些成员来自于 WebClient 类:

public byte[] DownloadData (Uri address);    // 同步版本
public void DownloadDataAsync (Uri address);
public void DownloadDataAsync (Uri address, object userToken);
public event DownloadDataCompletedEventHandler DownloadDataCompleted;

public void CancelAsync (object userState);  // 取消一个操作
public bool IsBusy { get; }                  // 指示是否仍在运行
继续阅读...

C#中的多线程 - 同步基础

原文:http://www.albahari.com/threading/part2.aspx

专题:C#中的多线程

同步概要

第 1 部分:基础知识中,我们描述了如何在线程上启动任务、配置线程以及双向传递数据。同时也说明了局部变量对于线程来说是私有的,以及引用是如何在线程之间共享,允许其通过公共字段进行通信。

下一步是同步(synchronization):为期望的结果协调线程的行为。当多个线程访问同一个数据时,同步尤其重要,但是这是一件非常容易搞砸的事情。

同步构造可以分为以下四类:

简单的阻塞方法
这些方法会使当前线程等待另一个线程结束或是自己等待一段时间。SleepJoinTask.Wait都是简单的阻塞方法。
锁构造
锁构造能够限制每次可以执行某些动作或是执行某段代码的线程数量。排它锁构造是最常见的,它每次只允许一个线程执行,从而可以使得参与竞争的线程在访问公共数据时不会彼此干扰。标准的排它锁构造是lockMonitor.Enter/Monitor.Exit)、MutexSpinLock。非排它锁构造是SemaphoreSemaphoreSlim以及读写锁
信号构造
信号构造可以使一个线程暂停,直到接收到另一个线程的通知,避免了低效的轮询 。有两种经常使用的信号设施:事件等待句柄(event wait handle )Monitor类的Wait / Pluse方法。Framework 4.0 加入了CountdownEventBarrier类。
非阻塞同步构造
非阻塞同步构造通过调用处理器指令来保护对公共字段的访问。CLR 与 C# 提供了下列非阻塞构造:Thread.MemoryBarrierThread.VolatileReadThread.VolatileWritevolatile关键字以及Interlocked类。
继续阅读...

C#中的多线程 - 基础知识

原文:http://www.albahari.com/threading/

专题:C#中的多线程

简介及概念

C# 支持通过多线程并行执行代码,线程有其独立的执行路径,能够与其它线程同时执行。

一个 C# 客户端程序(Console 命令行、WPF 以及 Windows Forms)开始于一个单线程,这个线程(也称为“主线程”)是由 CLR 和操作系统自动创建的,并且也可以再创建其它线程。以下是一个简单的使用多线程的例子:

所有示例都假定已经引用了以下命名空间:

using System;
using System.Threading;
class ThreadTest
{
  static void Main()
  {
    Thread t = new Thread (WriteY);  // 创建新线程
    t.Start();                       // 启动新线程,执行WriteY()

    // 同时,在主线程做其它事情
    for (int i = 0; i < 1000; i++) Console.Write ("x");
  }

  static void WriteY()
  {
    for (int i = 0; i < 1000; i++) Console.Write ("y");
  }
}
继续阅读...

程序单一实例实现

不少应用程序有单一实例的需求,也就是同时只能开启一个实例(一般也就是一个进程)。

实现的方式可能有判断进程名字,使用特殊文件等等,但是最靠谱的方式还是使用系统提供的 Mutex 工具。

Mutex是互斥体,命名的互斥体可以跨进程使用,所以可以用以实现程序单一实例这个需求。相关的例子网上应该不少,不过很多给出的例子中并没有注意到一些细节,这里就完整总结下。

继续阅读...