多线程编程之计算限制型异步操作

本文介绍CLR线程池的工作原理及线程执行上下文的流转方式,并提供线程池常见应用场景示例,包括向线程池添加线程、协作式取消线程等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. CLR线程池简介

1.1CLR为什么支持线程池

1.2线程池ThreadPool管理线程

2. 线程执行上下文

2.1线程执行上下文简介

2.2一个简单示例

3.线程池常见应用情景示例

3.1 将一个线程添加至线程池中(向线程传递参数)

3.2 协作式取消

4. Task简介

5. Task编程基础

6. 定时器Timer

<1>. CLR线程池简介

1.1 CLR为什么支持线程池?

上一篇中讲到如果在一个应用程序中启动了多个线程的话,显然是会影响到程序的运行效率,一种很直观的想法是:如果一个线程完成了任务,我们并不在内存中销毁这个线程的实例,而是将这个线程进入空闲状态,如果又来了一个请求,可以不用另外创建新线程,这样显然能够带来性能上的提升,在.net中,CLR为我们实现了线程池,它可以看作是线程的一个集合。每个CLR都存在一个全局的线程池,供所有的AppDomain共享。

1.2 线程池ThreadPool管理线程

System.Threading.ThreadPool中提供了查询和设置线程池性质的的静态方法。

public static bool SetMaxThreads(int workerThreads, int completionPortThreads); // 设置线程池中线程数量的最大值,但是永远不要折磨做,容易发生死锁等情况

public static bool SetMinThreads(int workerThreads, int completionPortThreads); // 设置线程池中线程数量的最小值

public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads);// 返回线程池支持的最大线程数和当前活动的线程的差值

<2>. 线程执行的上下文及其流转

2.1 线程执行上下文简介

线程的执行上下文包含安全设置,宿主设置,线程上下文数据(System.Runtime.Remoting.Messaging.CallContext的LogicalSetData方法)等信息。当一个线程使用另外的线程执行任务时,前者的线程执行上下文将流转至后者的线程执行上下文中。在c#中类型System.Threading.ExecutionContext来控制线程执行上下文的流转:

public static AsyncFlowControl SuppressFlow(); // 阻止(Suppress本意是抑制)线程执行上下文的流转

public static bool IsFlowSuppressed(); // 判断当先线程执行上下文是否被阻止

2.2 一个简单示例

static void Main(string[] args)
{
// 见一些数据保存在Main线程的执行上下文中
CallContext.LogicalSetData("myname", "xuqiang");
// 线程执行上下文流转,所以这里是能够得到myname的值的
ThreadPool.QueueUserWorkItem
(
state =>
{
// 将输出xuqiang
Console.WriteLine("MyName :" + CallContext.LogicalGetData("myname"));
}
);
// 现在阻止线程上下文的流动
ExecutionContext.SuppressFlow();
// 下面的线程将无法访问Main线程的上下文
ThreadPool.QueueUserWorkItem
(
state =>
{
// 不会输出xuqiang
Console.WriteLine("MyName :" + CallContext.LogicalGetData("myname"));
}
);
// 回复上下文流转
ExecutionContext.RestoreFlow();
// 等待线程执行完毕
Thread.Sleep(1000);
Console.WriteLine("Press any key...");
Console.ReadKey();

}

通过上面的建立示例,我们能够明确,

1. 如何向线程池中添加工作线程:

通过调用QueueUserWorkItem,该静态方法存在两个重载方法:

public static bool QueueUserWorkItem(WaitCallback callBack); // 不需要传递参数

public static bool QueueUserWorkItem(WaitCallback callBack, object state); // 需要向线程传递参数

2. 通过函数SuppressFlown能够阻止线程上下文的流转,通过RestoreFlow能够恢复线程上下文

3. 通过Thread.Sleep函数能够阻塞调用Thread.Sleep的线程(这里是指Main线程)

<3>. 线程池常见应用场景示例

3.1 将一个线程添加至线程池中(向线程中传递参数)

static void Main(string[] args)
{
Console.WriteLine("Main thread: queuing an asychronous operation");
// 向线程池中添加一个工作线程,同时传递参数5
ThreadPool.QueueUserWorkItem(ComputeOp, 5);
Console.WriteLine("Main thread: Doing other work here..");
// 将main线程休眠一段时间,以保证computeOp执行完成
Thread.Sleep(10000); // 10秒
Console.WriteLine("Hit <Enter> to end this program");
Console.ReadKey();
}
private static void ComputeOp(object state)
{
Console.WriteLine("CompiteOp: state is " + state);
// 模拟工作1s
Thread.Sleep(1000);
// 这个线程返回到线程池中,将等待另外的任务

}

3.2 协作式取消

之所以称之为“协作式取消”,是讲一个线程T如果想要获得被取消的能力的话,那么T需要接受一个类型为CancellationToken参数(该类型存在于.net 4.0中,3.5中不存在),在T中通过查询CancellationToken的IsCancellationRequested属性来判断是否已经被取消。下面结合这个场景来分析这个过程中使用到的类CancellationTokenSource和CancellationToken:

public sealed class CancellationTokenSource : IDisposable

{

public CancellationTokenSource(); // 默认构造函数

public bool IsCancellationRequested { get; } // 查询线程是否已经被取消

public CancellationToken Token { get; } // 构造好了CancellationTokenSource通过该方法得到CancellationToken实例,然后传递给需要实现协作式取消的线程T

public void Cancel(); // 通过该方法取消T线程

// 取消线程T的重载版本,可以通过CancellationTokenSource的CancellationToken属性的Register注册委托,在Cancel方法调用后执行。如

//果throwOnFirstException为true的话,那么在注册的方法中的任何一个方法抛出未处理异常,Cancel将抛出该异常 ,同时阻止其他回调方法

//的调用,如果throwOnFirstException为false的话,如果在任何的回调方法中抛出异常不会影响到其他回调方法的调用,未处理的异常将添加

// 至AggregateException中

public void Cancel(bool throwOnFirstException);

public void Dispose(); // 实现IDisposable接口

// 连接另外一组CancellationToken来创建CancellationTokenSource示例,例如如果通过var cts =CreateLinkedTokenSource(token1, token2)

// 那么如果token1或者token2取消,cts将被取消

public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens);

// 重载版本

public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2);

}

public struct CancellationToken

{

public bool IsCancellationRequested { get; } // 得到是否已经存在对于该CancellationToken的取消操作

public static CancellationToken None { get; } // 如果一个工作进程不想被取消的话,可以使用该静态字段

// 下面的几个方法将向CancellationToken中注册委托时间,如果CancellationToken被取消,将调用这些方法

public CancellationTokenRegistration Register(Action callback);

public CancellationTokenRegistration Register(Action<object> callback, object state);

// 该方法主要解决在winform,wpf等中,工作线程如何更新ui线程

public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);

....

}

这个示例演示了如何使用CancellationTokenSource,并向其中注册回调方法,如何管理异常等特性:

static void Main(string[] args)
{
Go();
}
public static void Go()
{
// 生成CancellationTokenSource对象
CancellationTokenSource cts = new CancellationTokenSource();
// 向cts中添加回调事件,但token调用cancel方法时,将调用这个方法
cts.Token.Register( WhenCancel1 );
// 将抛出异常
cts.Token.Register(WhenCancel2 );
cts.Token.Register(WhenCancel3);
// 向线程队列中添加一个工作线程
ThreadPool.QueueUserWorkItem
(
o => Count(cts.Token, 1000)
);
Console.WriteLine("Press enter to cancel the operation .");
Console.ReadLine();
try
{
// 如果为true的话,将仅仅调用WhenCancel3方法
// 如果为false将调用这三个回调方法
cts.Cancel(true);
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
}
private static void WhenCancel1()
{
Console.WriteLine("When cancel 1 delegate called.");
}
private static void WhenCancel2()
{
// Console.WriteLine("When cancel 3 delegate called.");
throw new Exception();
}
private static void WhenCancel3()
{
Console.WriteLine("When cancel 3 delegate called.");
}
private static void Count(CancellationToken token, Int32 countTo)
{
for (Int32 count = 0; count < countTo; ++count )
{
// 如果被取消
if(token.IsCancellationRequested)
{
Console.WriteLine("The operation is cancel.");
break;
}
// 打印
Console.WriteLine(count);
// 休眠该线程一段时间
Thread.Sleep(200);
}
Console.WriteLine("Count is done.");

}

下面的示例将演示如何实现一个链接的CancellationTokenSource

public static void Go()
{
// 生成CancellationTokenSource对象
CancellationTokenSource cts1 = new CancellationTokenSource();
cts1.Token.Register(() => Console.WriteLine("cts1 is cancel"));
CancellationTokenSource cts2 = new CancellationTokenSource();
cts2.Token.Register(() => Console.WriteLine("cts2 is cancel"));
// 创建链接
CancellationTokenSource linkedCts =
CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);
// 如果取消cts1或者是cts2中的一个,那么linkedCts将被取消
cts1.Cancel();
Console.WriteLine("cts1 cancel = {0}, cts2 cancel = {1}",
cts1.IsCancellationRequested,
cts2.IsCancellationRequested);
Console.WriteLine("Press enter to cancel the operation .");
Console.ReadLine();

}

下一篇继续...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值