C# .Net学习笔记—— 异步和多线程(Thread)

一、简单案例

cs 复制代码
      private void buttonThreads_Click(object sender, EventArgs e)
        {
            Log.Info($"btnThread_Click_Start {Thread.CurrentThread.ManagedThreadId}");
            ThreadStart threadStart = () => this.DoSomethingLong("btnThread_Start");
            Thread thread = new Thread(threadStart);
            thread.Start();
            Log.Info($"btnThread_Click_End {Thread.CurrentThread.ManagedThreadId}");
        }

        private void DoSomethingLong(string name)
        {
            Log.Info($"DoSomethingLong Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            long result = 0;
            for (int i = 0; i < 10000000; i++)
            {
                result += i;
            }
            Thread.Sleep(2000);
            Log.Info($"DoSomethingLong End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        }

注意:现在不建议使用thread.Suspend();//线程挂起

thread.Resum(); //唤醒线程

thread.Abort(); //销毁,方法是抛异常,也不建议使用

二、常用API介绍

1、Join 线程等待
cs 复制代码
thread.Join(500);//最多等待500毫秒
thread.Join();   //当前线程等待thread完成
2、thread.ThreadState; //线程状态
3、thread.IsBackground;

(i)默认是前台线程,启动之后一定要完成任务的,阻止进程退出。

(ii)如果使用后台线程,只要进程退出了线程也就退出了,现在一般都建议使用后台线程,因为不会卡界面。

4、thread.Priority;

(i)线程优先级,可以设置5个等级 CPU会优先执行ThreadPriority。

(ii)但是不代表它会最先完成

二、Thread的问题

线程池--享元模式--数据库连接池

1、thread提供了太多的API

2、无限使用线程,需要加以限制

3、重用线程,避免重复的创建和销毁

三、线程池

cs 复制代码
       private void buttonQueue_Click(object sender, EventArgs e)
        {
            Log.Info($"DoSomethingLong Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            ThreadPool.QueueUserWorkItem(t => this.DoSomethingLong("btnThreadPool_Click"));
            Log.Info($"DoSomethingLong End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        }

        private void DoSomethingLong(string name)
        {
            Log.Info($"DoSomethingLong Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            long result = 0;
            for (int i = 0; i < 10000000; i++)
            {
                result += i;
            }
            Thread.Sleep(2000);
            Log.Info($"DoSomethingLong End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        }
1、手动限制线程池内的数量
cs 复制代码
        private void buttonQueue_Click(object sender, EventArgs e)
        {
            Log.Info($"DoSomethingLong Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            ThreadPool.QueueUserWorkItem(t => this.DoSomethingLong("btnThreadPool_Click"));

            {
                ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
                Log.Info($"线程池中辅助线程最大数目={workerThreads} 线程池中异步IO线程的最大数目={completionPortThreads}");
            }
            {
                ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
                Log.Info($"线程池中辅助线程最小数目={workerThreads} 线程池中异步IO线程的最小数目={completionPortThreads}");
            }

            ThreadPool.SetMaxThreads(16, 16);
            ThreadPool.SetMinThreads(8, 8);

            {
                ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
                Log.Info($"线程池中辅助线程最大数目={workerThreads} 线程池中异步IO线程的最大数目={completionPortThreads}");
            }
            {
                ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
                Log.Info($"线程池中辅助线程最小数目={workerThreads} 线程池中异步IO线程的最小数目={completionPortThreads}");
            }

            Log.Info($"DoSomethingLong End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        }

ThreadPool啥也没有,不能控制线程的开始,暂停,恢复等功能

四、线程池等待

1、类 包含了一个bool属性

初始化为false -- WaitOne 等待 -- Set -- true--WaitOne直接过去

true--WaitOne直接过去--Reset -- false -- WaitOne等待

cs 复制代码
        private void buttonQueue_Click(object sender, EventArgs e)
        {
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(t =>
            {
                this.DoSomethingLong("btnThreadPool_Click");
                manualResetEvent.Set();
            });
            manualResetEvent.WaitOne();
        }

五、线程池阻塞(死锁)

cs 复制代码
private void button小案例_Click(object sender, EventArgs e)
        {
            {
                ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
                Console.WriteLine(workerThreads + " " + completionPortThreads);
            }

            ThreadPool.SetMaxThreads(20, 20);
            ThreadPool.SetMinThreads(8, 8);

            
            {
                ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
                Console.WriteLine(workerThreads + " " + completionPortThreads);
            }

            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            for (int i = 0; i < 28; i++)
            {
                int k = i;
                ThreadPool.QueueUserWorkItem(t =>
                {
                    Console.WriteLine(k);
                    if (k < 20) 
                    {
                        manualResetEvent.WaitOne();
                    }
                    else 
                    {
                        manualResetEvent.Set();
                    }
                });
            }
            if (manualResetEvent.WaitOne()) 
            {
                Console.WriteLine("没有死锁");
            }
        }

这段代码里我限定了线程池里边只有20个线程,而我for循环里调用了超过20个,所以产生了线程死锁问题。

**原因:**我们在使用阻塞方法的时候没有把线程Set回去

cs 复制代码
private void buttonQueue_Click(object sender, EventArgs e)
        {
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(t =>
            {
                this.DoSomethingLong("btnThreadPool_Click");
                //这里应该Set回去
                manualResetEvent.Set();
            });
            manualResetEvent.WaitOne();
        }
相关推荐
葬歌倾城7 小时前
JSON的缩进格式方式和紧凑格式方式
c#·json
码荼7 小时前
学习开发之hashmap
java·python·学习·哈希算法·个人开发·小白学开发·不花钱不花时间crud
武昌库里写JAVA8 小时前
Oracle如何使用序列 Oracle序列使用教程
java·开发语言·spring boot·学习·课程设计
祁思妙想8 小时前
八股学习(三)---MySQL
数据库·学习·mysql
Eiceblue9 小时前
使用 C# 发送电子邮件(支持普通文本、HTML 和附件)
开发语言·c#·html·visual studio
小小小小王王王9 小时前
hello判断
开发语言·c#
amazinging9 小时前
北京-4年功能测试2年空窗-报培训班学测开-第四十一天
python·学习·appium
Jyywww1219 小时前
微信小程序学习笔记
笔记·学习·微信小程序
amazinging9 小时前
北京-4年功能测试2年空窗-报培训班学测开-第三十九天
python·学习·appium
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频