C#多线程之线程基础篇( 四 )

Interrupt和Abort最大的不同是:调用Interrupt线程会继续工作直到下次被阻塞时抛出异常,而调用Abort会立即在线程正在执行的地方抛出异常(非托管代码除外) 。
这将导致一个新的问题:.NET Framework 中的代码可能会被中止,而且不是安全的中止 。如果中止发生在FileStream被构造期间,很可能造成一个非托管文件句柄会一直保持打开直到应用程序域结束 。
协作取消模式正如上面所说Interrupt和Abort总是危险的,替代方案就是实现一个协作模式(cooperative ):工作线程定期检查一个用于指示是否应该结束的标识,发起者只需要设置这个标识 , 等待工作线程响应,即可取消线程执行 。
Framework 4.0 提供了两个类CancellationTokenSource和CancellationToken来完成这个模式:

  • CancellationTokenSource定义了Cancel方法 。
  • CancellationToken定义了IsCancellationRequested属性和ThrowIfCancellationRequested方法 。
void 取消令牌(){var cancelSource = new CancellationTokenSource();cancelSource.CancelAfter(3000);var t = new Thread(() => Work(cancelSource.Token));t.Start();t.Join();}void Work(CancellationToken cancelToken){while (true){cancelToken.ThrowIfCancellationRequested();// ...Thread.Sleep(1000);}}四、异步编程模式MSDN文档:https://learn.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/
异步编程模型(APM)异步编程模型(Asynchronous Programming Model),提出于.NET Framework 1.x 的时代,基于IAsyncResult接口实现类似BeginXXX和EndXXX的方法 。
APM是建立在委托之上的,Net Core中的委托 不支持异步调用 , 也就是 BeginInvoke 和 EndInvoke 方法 。
void APM(){var uri = new Uri("https://www.albahari.com/threading/part3.aspx");Func<Uri, int> f = CalcUriStringCount;var res = f.BeginInvoke(uri, null, null);// do something_testOutputHelper.WriteLine("我可以做别的事情");_testOutputHelper.WriteLine("共下载字符数:" + f.EndInvoke(res));}int CalcUriStringCount(Uri uri){var client = new WebClient();var res = client.DownloadString(uri);return res.Length;}EndInvoke会做三件事:
  1. 如果异步委托还没有结束,它会等待异步委托执行完成 。
  2. 它会接收返回值(也包括refout方式的参数) 。
  3. 它会向调用线程抛出未处理的异常 。
不要因为异步委托调用的方法没有返回值就不调用EndInvoke,因为这将导致其内部的异常无法被调用线程察觉 。MSDN文档中明确写了 “无论您使用何种方法,都要调用 EndInvoke 来完成异步调用 。”
BeginInvoke也可以指定一个回调委托 。这是一个在完成时会被自动调用的、接受IAsyncResult对象的方法 。
BeginInvoke的最后一个参数是一个用户状态对象,用于设置IAsyncResultAsyncState属性 。它可以是需要的任何东西,在这个例子中,我们用它向回调方法传递method委托,这样才能够在它上面调用EndInvoke
var uri = new Uri("https://www.albahari.com/threading/part3.aspx");Func<Uri, int> func = CalcUriStringCount;var res = func.BeginInvoke(uri, new AsyncCallback(res =>{var target = res.AsyncState as Func<string, int>;_testOutputHelper.WriteLine("共下载字符数:" + target!.EndInvoke(res));_testOutputHelper.WriteLine("异步状态:" + res.AsyncState);}), func);// do something_testOutputHelper.WriteLine("我可以做别的事情");func.EndInvoke(res);基于事件的异步模式(EAP)基于事件的异步模式(event-based asynchronous pattern),EAP 是在 .NET Framework 2.0 中提出的,让类可以提供多线程的能力,而不需要使用者显式启动和管理线程 。这种模式具有以下能力:
  • 协作取消模型(cooperative cancellation model)
  • 线程亲和性(thread affinity)
  • 将异常转发到完成事件(forwarding exceptions)
这个模式本质上就是:类提供一组成员,用于在内部管理多线程 , 类似于下边的代码:
// 这些成员来自于 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; }// 指示是否仍在运行当调用基于EAP模式的类的XXXAsync方法时,就开始了一个异步操作,EAP模式是基于APM模式之上的 。

推荐阅读