C# 多线程学习 (一)--- 线程池VS任务

1. 线程池

.Net线程池是由CLR管理的线程集合,用户可以使用线程池创建线程,而创建线程的数量,销毁线程的时机均由CLR管理。
线程池适用于处理时间较短的线程操作,可以节省系统反复创建、删除线程的开销。CLR初始化时,线程池中没有线程,直至线程池所维护的操作请求队列出现第一个异步操作记录项,线程池创建第一个线程。该线程执行完毕后,线程不会立即删除,CLR会对空闲线程进行删除。
线程池不适用于长时间计算密集性操作,同时,缺乏通知异步操作完成,以及返回操作结果的机制。

创建

 public static void AsynThreadPool()
 {
      const int x = 1;
      const int y = 2;
      const string lambdaState = "lambda state 2";
      //先在线程池放入函数
      System.Threading.ThreadPool.QueueUserWorkItem(AsyncOperation);
      Sleep(TimeSpan.FromSeconds(1));
      // 再次放入参数
      System.Threading.ThreadPool.QueueUserWorkItem(AsyncOperation, "asyncState");
      // Sleep(TimeSpan.FromSeconds(1));
      //lambda
      System.Threading.ThreadPool.QueueUserWorkItem(state =>
      {
          Console.WriteLine("Operation state" + state.ToString());
          Console.WriteLine("Worker thread id" + CurrentThread.ManagedThreadId.ToString());
          //  Sleep(TimeSpan.FromSeconds(2));
      }, "lambda state");
      //闭包
      System.Threading.ThreadPool.QueueUserWorkItem(_ =>
      {
          Console.WriteLine("Operation state" + x.ToString() + "+" + y.ToString() + "," + lambdaState.ToString());
          Console.WriteLine("Worker thread id:" + CurrentThread.ManagedThreadId.ToString());
      }, "lambda state2");
      Sleep(TimeSpan.FromSeconds(2));
  }

取消

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
     // 注册 cts.Cancel() 执行后的回调方法
    cts.Token.Register(() => { Console.WriteLine(" CancellationTokenSource is Register"); });
    System.Threading.ThreadPool.QueueUserWorkItem(_=> 
    {
        Sleep(1000);
        if (cts.IsCancellationRequested)
        {
            Console.WriteLine("Sleep 1000s, Thread is cancel");
            return;
        }
        Console.WriteLine("Sleep 1000s, Thread isn't cancel");
    });
    System.Threading.ThreadPool.QueueUserWorkItem(_ =>
    {
        Sleep(100);
        if (cts.IsCancellationRequested)
        {
            Console.WriteLine("Sleep 100s, Thread is cancel");
            return;
        }
        Console.WriteLine("Sleep 100s, Thread isn't cancel");
    });
    System.Threading.ThreadPool.QueueUserWorkItem(_ =>
    {
        if (cts.IsCancellationRequested)
        {
            Console.WriteLine("Thread is cancel");
            return;
        }
        Console.WriteLine("Thread isn't cancel");
    });
    Thread.Sleep(10);
    cts.Cancel();
    Console.ReadLine();
}

也可用cts.Token 作为参数传入异步方法

2.任务

比线程池更抽象的概念,可以通过线程或不通过线程实现并发操作。Task 概念由 .Net Framework4.0 引入,是并行编程库(Task Parallel Library,简称 TPL)的一部分。
Task 是可组合的—利用延续,可同时启动多个任务,待所有任务执行完毕后,启动另一个任务处理之前任务的结果。

启动

Task.Run(()=>{ Console.WriteLine("Start Task");});

var task2 = new Task(() => Console.WriteLine("Start Task"));
task2.Start();

Task.Factory.StartNew(() => Console.WriteLine("Start Task"));

等待

var task = Task.Run(() => Console.WriteLine("Start Task"));
//显式阻塞当前线程,等待task执行完毕
task.Wait();

获得任务完成的结果

Task<Int32> t = new Task<int>(n =>
{
    Thread.Sleep(1000);
    var result = 0;
    var input = int.Parse(n.ToString());
    for (int i = 0; i < input; i++)
    {
        result += i;
    }
    return result;
}, 100000);
Console.WriteLine($"Task has started!");
t.Start();
//阻塞当前线程,等待任务返回结果
Console.WriteLine($"Task result is {t.Result}");
Console.WriteLine($"Current thread has been blocked");

取消任务

// 利用 CancellationTokenSource 作为取消标志,取消任务。
CancellationTokenSource cts = new CancellationTokenSource();
Task<Int32> t = new Task<int>(() =>
{
    var result = 0;
    for (int i = 0; i < 10000; i++)
    {
        if (cts.IsCancellationRequested)
            return result;
        result += i;
        Thread.Sleep(2);
    }
    return result;
});
Console.WriteLine($"Task has started!");
t.Start();
Thread.Sleep(10);
Console.WriteLine($"cts has been canceled");
cts.Cancel();
try
{
    // 捕获异常后,打印任务结果
    Console.WriteLine($"Task result is {t.Result}");
}
catch (AggregateException ex)
{
    //取消任务后,会抛出OperationCanceledException
    Console.WriteLine($"Task has been canceled");
}

CancellationToken 可以直接传入Task匿名函数中,为什么重写Task函数,将CancellationToken 作为参数传入?
因为当任务开始前,取消标志位为已取消状态,通过参数传递,会在Task.Start()执行时得到InvalidOperationException异常。而Task操作中调用CancellationToken , 是巡检在任务执行过程中, CancellationToken 时候置为取消状态。

public Task(Func<TResult> function, CancellationToken cancellationToken);

并行运行任务

这是任务较于线程池具有优势的地方,采用Task.WhenAll()等待所有任务执行完毕后,执行下一个操作;
采用Task.WhenAny(),等待任意一个任务完成或由于超时而结束后,执行下一个操作。此处,可以借助取消标志位一起,优化任务执行超时的问题。

var task1 = Task<string>.Run(()=> { Thread.Sleep(100); return "Start1 Task"; });           
var task2 = Task<string>.Run(()=> { Thread.Sleep(10); return "Start2 Task"; });

var task3 = Task.WhenAll(new[] { task1, task2 });
task3.ContinueWith(t3 => { Console.WriteLine($"{t3.Result[0]}--{t3.Result[1]} are All Done !"); });

var task4 = Task.WhenAny(new[] { task1, task2 });
task4.ContinueWith(t4 => { Console.WriteLine(t4.Result.Result+" is First !"); });

执行长任务

采用TaskCreationOptions.LongRunning, 提议TaskScheduler(任务调度程序)避免使用池化线程执行长时间的计算密集型操作。

3. 资源集合

《果壳中的C#》《CLR via C#》《C#多线程编程实战》
14.并发与异步 - 3.C#5.0的异步函数 -《果壳中的c#》
C# 5.0 Async函数的提示和技巧

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值