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();
        }
相关推荐
小白郭莫搞科技5 小时前
鸿蒙跨端框架Flutter学习:CustomTween自定义Tween详解
学习·flutter·harmonyos
阳光九叶草LXGZXJ6 小时前
达梦数据库-学习-47-DmDrs控制台命令(LSN、启停、装载)
linux·运维·数据库·sql·学习
A9better7 小时前
嵌入式开发学习日志53——互斥量
stm32·嵌入式硬件·学习
进阶小白猿8 小时前
Java技术八股学习Day30
java·开发语言·学习
近津薪荼8 小时前
优选算法——双指针6(单调性)
c++·学习·算法
修修修也9 小时前
【无标题】技术欲望是怎样渐进增长的?
学习
whale fall9 小时前
celery -A tool.src.main worker --loglevel=info --queues=worker1_queue & 什么意思
python·学习·apache
wotaifuzao10 小时前
【Keil 5安装】keil 5最新版本安装+环境配置+下载百度资源分享(安装包,注册机等)
stm32·单片机·嵌入式硬件·mcu·学习·keil5·最新keil
初九之潜龙勿用12 小时前
C# 操作Word模拟解析HTML标记之背景色
开发语言·c#·word·.net·office
31087487612 小时前
0005.C/C++学习笔记5
c语言·c++·学习