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();
        }
相关推荐
汤姆和佩琦30 分钟前
2024-12-25-sklearn学习(20)无监督学习-双聚类 料峭春风吹酒醒,微冷,山头斜照却相迎。
学习·聚类·sklearn
好学近乎知o41 分钟前
正则表达式(学习Django过程中可能涉及的)
学习·正则表达式·django
雨中奔跑的小孩44 分钟前
爬虫学习案例8
爬虫·学习
jieshenai1 小时前
使用 VSCode 学习与实践 LaTeX:从插件安装到排版技巧
ide·vscode·学习
灰太狼不爱写代码4 小时前
CUDA11.4版本的Pytorch下载
人工智能·pytorch·笔记·python·学习
eybk9 小时前
Pytorch+Mumu模拟器+萤石摄像头实现对小孩学习的监控
学习
6.949 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
VinciYan10 小时前
基于Jenkins+Docker的自动化部署实践——整合Git与Python脚本实现远程部署
python·ubuntu·docker·自动化·jenkins·.net·运维开发
码农君莫笑10 小时前
使用blazor开发信息管理系统的应用场景
数据库·信息可视化·c#·.net·visual studio
守护者17011 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习