主要内容概要
线程池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("执行成功!");
}