C# Advanced Tutorial 1-12-Threads01


主要内容概要

线程池ThreadPool

Thread

C#中的多线程 1.0已经存在
Thread类: C#对线程对象的一个封装
ThreadStart是一个没有参数没有返回值的委托:public delegate void ThreadStart();

ThreadStart threadStart = () =>
{
  Thread.Sleep(5000);
  this.DoSomethingLong("btnThread_Click");
  Console.WriteLine($"****************btnThread_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
};
Thread thread = new Thread(threadStart);
thread.Start(); //开启一个新线程  
thread.Suspend();// 暂停线程 弃用了
thread.Resume();//恢复  无法实时的去暂停或者恢复线程 弃用了
thread.Abort();//终结线程
Thread.ResetAbort();//都会有延时

上述例子描述了启动一个线程,在这个线程里执行委托内容。另外一种使用ParameterizedThreadStart也是类似的。
thread.Abort(),线程是计算机资源,程序想停下线程,只能向操作系统通知(线程抛异
常),会有延时/不一定能真的停下来。
暂停不一定马上暂停,不该让线程操作太复杂。

ParameterizedThreadStart threadStart = ar =>
{
  this.DoSomethingLong("btnThread_Click");
  Console.WriteLine($"****************btnThread_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
};
Thread thread = new Thread(threadStart);
thread.Start(); //开启一个新线程

线程等待:
如果我们需要等待;

  • 等待
while (thread.ThreadState != ThreadState.Stopped)
{
  Thread.Sleep(200);
} 
  • Join等待
thread.Join();//可以限时等待 运行这句话代码的线程,等待thread的完成
thread.Join(2000); //可以限时等待

线程优先级

thread.Priority = ThreadPriority.Highest;//是不是就可以保证是优先执行?
//只是增加他的优先概率;并不能一定的

后台线程

thread.IsBackground = true;//为后台线程  进程结束,线程结束了
thread.IsBackground = false; //前台线程   进程结束后,任务执行完毕以后,线程才结束

线程回调
基于thread封装一个回调;
回调:启动子线程执行动作A——不阻塞——A执行完成后子线程会执行动作B
先看下面这个写法是否可以?

private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback)
{
  Thread thread = new Thread(threadStart);
  thread.Start();
  thread.Join();//错了 因为方法会阻塞
  actionCallback.Invoke();
}

显然是不可以的,join方法会阻塞主线程,得不到回调的效果。可以像下面这么写:

ThreadStart threadStart1 = new ThreadStart(() =>
{
  threadStart.Invoke();
  actionCallback.Invoke();
});
Thread thread = new Thread(threadStart1);
thread.Start();

带返回值的线程回调
要求:既是异步的,又要获取到返回结果?
既要计算又要非阻塞,想想看这个事儿也觉得不太可能。所以这里我们学习BeginInvoke和EndInvoke的做法,不直接返回计算结果,而是返回一个委托,然后在需要计算的时候再去调用委托。

private Func<T> ThreadWithReturn<T>(Func<T> func)
{
  T t = default(T);
  ThreadStart threadStart = new ThreadStart(() =>
  {
    t = func.Invoke();
  });
  Thread thread = new Thread(threadStart);
  thread.Start();

  return new Func<T>(() =>
  {
    thread.Join();//不invoke的时候不会发生,调用的时候才会阻塞
    return t;
  });
}

如果返回一个委托,在需要执行结果的时候,再去执行这个委托。在使用的时候可以这样:

//Thread开启一个新的线程执行任务,如何获取返回结果:
Func<int> func = () =>
{
  Thread.Sleep(5000);
  return DateTime.Now.Year;
};
Func<int> FuncResult = this.ThreadWithReturn(func);
//这里可以做一些其他的事情
int iResult = FuncResult.Invoke();//如果需要得到执行结果,是必须要等待的

线程池

Thread 功能繁多,反而用不好;就像给四岁小孩一把热武器,反而会造成更大的伤害。
线程池 .Net Framework 2.0
在Thread中对线程的管理需要我们自己去从操作,在不断的开启线程和销毁中,存在很大的开销,为了让线程可以反复的使用,出现了池化思想!
如果某个对象创建和销毁大家比较高,同时这个对象还可以反复使用的,就需要一个池子。
保存多个这样的对象,需要的时候就直接从池子里面获取,用完之后就不用销毁,返回池子就好。(享元模式)
节约资源提升性能;此外,还能管控总数量,放置滥用。

ThreadPool.QueueUserWorkItem(o =>
{
  Console.WriteLine($"**************** {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
  this.DoSomethingLong("ThreadPool.QueueUserWorkItem1");//开启了一个线程
}); 

还可以参数参数,这个o就是state的object类型参数:

ThreadPool.QueueUserWorkItem(o =>
{
  Console.WriteLine($"第二个参数:{o}");
  this.DoSomethingLong("ThreadPool.QueueUserWorkItem1");//开启了一个线程
}, "TestState");

获取最大线程数
设置线程数量是全局,线程池是全局,Task,async/awit 都是来自于线程,不建议随便设置!
直接New Thread不受这个数量限制的,但是会占用线程池的线程数量。

ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);

Console.WriteLine($"当前电脑最大workerThreads={maxWorkerThreads},最大的completionPortThreads={maxCompletionPortThreads}");
Console.WriteLine($"当前电脑最小workerThreads={minWorkerThreads},最小的completionPortThreads={minCompletionPortThreads}");

Console.WriteLine("设置线程数量之后:");

ThreadPool.SetMaxThreads(2, 2); //这里在设置的时候,数量不能低于本计算机的 核数
ThreadPool.SetMinThreads(2, 2);

ThreadPool.GetMaxThreads(out int maxWorkerThreads1, out int maxCompletionPortThreads1);
ThreadPool.GetMinThreads(out int minWorkerThreads1, out int minCompletionPortThreads1);

Console.WriteLine($"当前电脑最大workerThreads={maxWorkerThreads1},最大的completionPortThreads={maxCompletionPortThreads1}");
Console.WriteLine($"当前电脑最小workerThreads={minWorkerThreads1},最小的completionPortThreads={minCompletionPortThreads1}");

基于线程池等待
ManualResetEvent false 关闭,set是打开(True),waitOne就能接受到信号。
Reset 是关闭,就等于false,waitOne就只能等待。

//线程等待:
//在线程池中,有一个开关式 
ManualResetEvent manualResetEvent = new ManualResetEvent(false); //开关关闭 
ThreadPool.QueueUserWorkItem(o =>
{
this.DoSomethingLong("btnThreadPool_Click1");
Thread.Sleep(3000);
manualResetEvent.Set();// 开关打开
});
Console.WriteLine("Do something else。。。");
manualResetEvent.WaitOne();//执行到这儿来的时候,我就等你给我发信号
Console.WriteLine("计算完成");

看个例子,会发生死锁,设置8个线程,永远到不了9,所以不会set manualResetEvent。
建议不要WaitOne阻塞。
Thread线程池 都是后台线程。

ThreadPool.SetMaxThreads(8, 8);//设置了最大的线程数量
ManualResetEvent manualResetEvent = new ManualResetEvent(false); //开关关闭  
for (int i = 0; i < 10; i++)
{
  int k = i;
  ThreadPool.QueueUserWorkItem(t =>
  {
    Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    if (k == 9)
    {
      manualResetEvent.Set();
    }
    else
    {
      manualResetEvent.WaitOne();
    }
  });

}
if (manualResetEvent.WaitOne())
{
  Console.WriteLine("执行成功!");
}

文章作者: Chaoqiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Chaoqiang !
评论
 上一篇
C# Advanced Tutorial 1-13-Threads02 C# Advanced Tutorial 1-13-Threads02
主要内容概要1 Task:Waitall WaitAny Delay2 TaskFactory:ContinueWhenAny ContinueWhenAll3 Parallel TaskTask 是.NetFramework3.0
下一篇 
C# Advanced Tutorial 1-11-Async C# Advanced Tutorial 1-11-Async
主要内容概要1 进程-线程-多线程,同步和异步2 委托启动异步调用3 多线程特点:不卡主线程、速度快、无序性4 异步的回调和状态参数5 异步等待三种方式6 异步返回值 一些概念 进程:计算机概念,程序运行在服务器占据的全部计算机的资源。 线
  目录