21多线程基本使用1
多线程
多个线程之间不会等待,就像ajax那样,代码从上往下执行,不会等待线程执行完了才执行下一行。
Thread基本使用
使用1、
button按钮单击事件里面写代码
Thread thread = new Thread(ThreadInvork);
thread.Start();
ThreadInvork方法如下:
public void ThreadInvork()
{
Console.WriteLine("Hello");
}
将Windows窗体应用改为控制台输出:
也就是运行的时候附带一个控制台输出
①项目名右键 -> 属性
②应用程序 -> 输出类型 -> 控制台应用程序
也可以直接在Thread的构造函数里写lambda表达式,因为它接受的是委托类型
Thread thread = new Thread(()=> { Console.WriteLine("Hello World"); });
使用2:
前台线程:
就像Winfrom窗体的窗体关闭前事件一样,触发这个事件就是想让窗体关闭前做一些事情,比如关窗体前,把所有的日志保持起来,把数据库用户最后使用的时间给记录一下等等。此时就可以用前台线程,把它放入窗体的关闭事件中,把上面说的事情让前台线程去做完。
比如:将上面的方法改为:
public void ThreadInvork()
{
while (true)
{
Console.WriteLine("Hello");
}
}
那么,此时,关闭了窗体,控制台都还在运行,而且里面循环也是一直在循环的。
button事件里的代码用Thread类套一下ThreadStart,将方法名放在ThreadStart的构造函数里,一般都是这样用。
private void btnThread_Click(object sender, EventArgs e)
{
ThreadStart threadStart = new ThreadStart(ThreadInvork);
Thread thread = new Thread(threadStart);
thread.Start();
}
后台线程:
通过Thread的IsBackground属性,可以将它改为一个后台线程,让它的生命周期和窗体一样,这样窗体程序关闭线程就关闭了。这个属性默认不设置是为false的,也就是说程序光退出也没用,只要线程还在运行,那么整个程序也不会关闭,于是这个线程就在后台悄悄运行着。button事件里的代码用Thread类套一下ThreadStart,把方法放在ThreadStart里面。
private void btnThread_Click(object sender, EventArgs e)
{
ThreadStart threadStart = new ThreadStart(ThreadInvork);
Thread thread = new Thread(threadStart);
thread.IsBackground = true;
thread.Start();
}
ThreadPool基本使用
Thread用的较少,用ThreadPool用的比较多,从线程池里面拿线程。这样所有的线程统一通过线程池去分配。而不是自己手动创建线程,是统一的从系统的线程池里面拿。
按钮的单击事件:
private void btnThreadPool_Click(object sender, EventArgs e)
{
//获取一个正在使用的工作线程,第一个参数是线程要执行的方法,第二个参数是被执行的方法的参数,被执行的方法必须要有一个参数
ThreadPool.QueueUserWorkItem(ThreadPoolInvork,"Hello World");
}
ThreadPoolInvork方法如下:
public void ThreadPoolInvork(object o)
{
Console.WriteLine(o.ToString());
}
也可以用lambda表达式代替方法:
ThreadPool.QueueUserWorkItem((o)=> { Console.WriteLine(o.ToString()); },"Hello World");
设置最大和最小线程数:
设置最多从线程池里取多少线程,也就是最大工作线程和最大异步线程池,通过这个方法设置最大可以使用的线程,这样就可以控制线程数,如果不停的拿线程会对计算机造成压力。
ThreadPool.SetMaxThreads(16, 8);
int maxW; int maxC;
ThreadPool.GetMaxThreads(out maxW, out maxC);//查看设置的线程数
Console.WriteLine(maxW + "_ _ _ " + maxC);
ThreadPool.SetMinThreads(1, 1);
int minW; int minC;
ThreadPool.GetMinThreads(out minW, out minC);
Console.WriteLine(minW + "_ _ _ " + minC);
线程池等待
//等待线程的开关
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
//开始这个线程执行
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("Hello");
manualResetEvent.Set();//打开线程,不让其等待
});
//开始等待上面线程执行完毕
manualResetEvent.WaitOne();//没有在上面Set的话,线程会在这里一直等待,下面代码也不会执行
Console.WriteLine("World");
Task
除了Thread有前台进程,其它线程都是后台线程(进程结束线程就结束)
Task基本使用:
普通调用
Task.Run(()=> { Console.WriteLine("Hello"); });
也可以直接传方法名调用:
Task.Run(MethodName);
实例化使用
//Task调用,
Task task = new Task(Test);
int i = 1;
if (i > 10)
task.Start();
Test方法结构:
public void Test()
TaskFactory
普通调用:
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(Test);
Parallel
主线程和子线程一起执行,这个多线程是带着子线程一起执行的,不常用。可以放多个符合委托的方法当参数。
用法:
Parallel.Invoke(
() => { Console.WriteLine(555); },
() => { Console.WriteLine(666); }
);
ParallelOptions
有两种用法:
For用法
ParallelOptions parallelOptions = new ParallelOptions();
//设置最多的一个线程数
parallelOptions.MaxDegreeOfParallelism = 6;//获取或者设置最大并发线程数
//因为上面设置了最大并发数,所以下面输出的时候是6个线程先完成,剩下两个在队列中等待,等上面6个线程用完了还回去后(释放掉了)再并发两个线程,也就是一次并行只能用6个线程。
Parallel.For(0, 8, parallelOptions, i =>
{
Console.WriteLine(i);//i就是0到8之间的数
});
ForEach用法
ParallelOptions parallelOptions = new ParallelOptions();
//设置最多的一个线程数
parallelOptions.MaxDegreeOfParallelism = 6;//获取或者设置最大并发线程数
Parallel.ForEach(new int[] { 1, 3, 5, 7, 9 }, parallelOptions, i =>
{
Console.WriteLine(i);
});
//如果上面int数组是一个人的类的数组,需要通过多线程来处理修改每个人的信息往数据库里存,就可以用ForEach并发处理修改,而不用像for的用法那样一个一个改。
线程之间的相互等待
如上图所示,想要输出最后的ok,虽然代码是从上往下执行,但是上面是多线程,线程不会等待,所以输出的结果如图:
可以看到,线程并不会等待,是异步执行的。
如果希望在线程结束后才执行后面的代码,常用的有下面两种方法:
Aync和await
并不是一个等待机制,但是有这个等待的效果,await本质上是当我们主线程看到这个关键字后,它就会回头,就回到自己的主线程中去,然后awt以下的所有代码又会被打包成一个新的委托,放到子线程里,当子线程之前执行的任务执行完毕后,再执行await以下的代码,它还是在子线程里面执行的。
WhenAll
在WhenAll后面输出:把要在多个进程结束后才执行的代码通过委托放在WhenAll里面。
List<Task> tasks = new List<Task>();
//Thread.CurrentThread.ManagedThreadId:当前线程的编号
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
tasks.Add(Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }));
Task.WhenAll(tasks).ContinueWith(//当集合中所有的线程结束后,通过ContinueWith执行结束后执行的东西
(o) =>
{
Console.WriteLine("ok WhenAll");
});
结果如下:ok WhenAll就在最后了
WaitAny
只要等待任何一个线程完成之后就可以执行下面的代码了,把集合转成数组,也就是只要这个数组里的线程只要有任意一个线程执行完毕后,就可以执行下面的代码了,并不代表第一个线程执行完了才执行后面的代码。
结果:
WaitAll
WaitAll也是和WhenAll一样,只不过这个用一句话作为分界线,不用把要执行的代码打包成委托放在方法里。这个和awiat很类似。
Task.WaitAll(tasks.ToArray());
Console.WriteLine("ok WaitAll");
委托的异步
Action action = delegate ()
{
Console.WriteLine("this is DelegateAsync");
};
IAsyncResult asyncResult = null;//接收异步委托的状态
for (int i = 0; i < 3; i++)
{
asyncResult = action.BeginInvoke(new AsyncCallback(o =>
{
Console.WriteLine("hahaha");
Console.WriteLine(o.AsyncState);
}), "FHZM");
bool waitWatch = asyncResult.AsyncWaitHandle.WaitOne();
}