21多线程基本使用1

多线程

多个线程之间不会等待,就像ajax那样,代码从上往下执行,不会等待线程执行完了才执行下一行。

Thread基本使用

使用1、

button按钮单击事件里面写代码

Thread thread = new Thread(ThreadInvork);
            thread.Start();

ThreadInvork方法如下:

 public void ThreadInvork()
        {
            Console.WriteLine("Hello");
        }

将Windows窗体应用改为控制台输出:
也就是运行的时候附带一个控制台输出
①项目名右键 -> 属性
②应用程序 -> 输出类型 -> 控制台应用程序
image
也可以直接在Thread的构造函数里写lambda表达式,因为它接受的是委托类型
Thread thread = new Thread(()=> { Console.WriteLine("Hello World"); });

使用2:

前台线程:

就像Winfrom窗体的窗体关闭前事件一样,触发这个事件就是想让窗体关闭前做一些事情,比如关窗体前,把所有的日志保持起来,把数据库用户最后使用的时间给记录一下等等。此时就可以用前台线程,把它放入窗体的关闭事件中,把上面说的事情让前台线程去做完。
比如:将上面的方法改为:

public void ThreadInvork()
        {            
            while (true)
            {
                Console.WriteLine("Hello");
            }
        }

image
那么,此时,关闭了窗体,控制台都还在运行,而且里面循环也是一直在循环的。

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的用法那样一个一个改。

线程之间的相互等待

image
如上图所示,想要输出最后的ok,虽然代码是从上往下执行,但是上面是多线程,线程不会等待,所以输出的结果如图:
image
可以看到,线程并不会等待,是异步执行的。
如果希望在线程结束后才执行后面的代码,常用的有下面两种方法:

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就在最后了
image

WaitAny

只要等待任何一个线程完成之后就可以执行下面的代码了,把集合转成数组,也就是只要这个数组里的线程只要有任意一个线程执行完毕后,就可以执行下面的代码了,并不代表第一个线程执行完了才执行后面的代码。
image
结果:
image

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();


            }
posted @ 2022-03-07 17:57  青仙  阅读(89)  评论(0编辑  收藏  举报