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();
        }
相关推荐
_李小白12 小时前
【Android Gradle学习笔记】第八天:NDK的使用
android·笔记·学习
William_cl12 小时前
【C# MVC 前置】异步编程 async/await:从 “卡界面” 到 “秒响应” 的 Action 优化指南(附微软官方避坑清单)
microsoft·c#·mvc
yong999012 小时前
C#驱动斑马打印机实现包装自动打印
java·数据库·c#
Jose_lz13 小时前
C#开发学习杂笔(更新中)
开发语言·学习·c#
QT 小鲜肉13 小时前
【个人成长笔记】Qt 中 SkipEmptyParts 编译错误解决方案及版本兼容性指南
数据库·c++·笔记·qt·学习·学习方法
A9better13 小时前
嵌入式开发学习日志41——stm32之SPI总线基本结构
stm32·单片机·嵌入式硬件·学习
mingupup13 小时前
WPF/C#:使用Microsoft Agent Framework框架创建一个带有审批功能的终端Agent
c#·wpf
Lynnxiaowen15 小时前
今天我们学习python编程常用模块与面向对象
运维·python·学习·云计算
慧都小妮子15 小时前
基于.NET UA Client SDK构建跨平台OPC UA客户端应用
.net·opc ua·automation·跨平台应用·unified
追逐时光者15 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 58 期(2025年10.13-10.19)
后端·.net