
C# 多线程
发布日期:2021-05-10 05:29:47
浏览次数:21
分类:精选文章
本文共 6358 字,大约阅读时间需要 21 分钟。
目录
1. 线程概述
1.1 线程的概念
- 线程是进程中的执行流程,一个进程可以有多个线程,每个线程可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线程。
1.2 多线程的优缺点
- 优点:
- 通过网络与Wed服务器的数据库进行通信;
- 执行占用大量时间的操作;
- 区分具有不同优先级的任务;
- 使用户界面可以在将时间分配给后台任务时仍能快速作出反应。
- 缺点:
- 系统将为进程和线程所需的上下文信息使用内存。创建进程和线程的数量会受到可用内存的限制;
- 跟踪大量的线程将占用大量的处理器时间。如果线程过多,则其中大多数线程都不会产生明显的进度。如果大多数线程处于一个进程中,则其他进程中的线程的调度频率就会低很多。
- 使用多个线程控制代码执行变得复杂;
- 销毁线程需要了解可能发生的问题并进行处理。
2. 线程的实现
2.1 Thread类创建线程
//移动的按钮using System.Threading;namespace ExThread1{ public partial class Form1 : Form { public Form1() { InitializeComponent(); CheckForIllegalCrossThreadCalls = false;//让线程可以调用窗体控件 } int x = 12; void Roll() { while(x<=260) { button1.Location = new Point(x, 12); Thread.Sleep(500); //线程休眠 x += 4; if(x>=260) { x = 12; } } } private void Form1_Load(object sender, EventArgs e) { Thread th = new Thread(new ThreadStart(Roll));//创建线程对象 th.Start(); //启动线程 } }}
2.2 线程的生命周期
3. 线程操作
3.1 线程的休眠
Thread.Sleep(500);//休眠500msThread.Sleep(-1);//无限期阻塞Thread.Sleep(Timeout.Infinite);//无限期阻塞
3.2 线程的加入
- 假如当前程序为多线程程序且存在一个线程A,现在需要插入线程B,并要求线程B执行完毕后,再继续执行线程A,此时可以用Thread类中的join方法实现。
3.3 线程的终止
- 线程的Abort方法用于永久地停止托管线程。调用Abort方法时,公共语言运行库在目标线程中引发ThreadAbortException异常,目标线程可以捕捉此异常。一旦线程被终止,它将无法重新启动。
3.4 线程的优先级
- 线程是根据优先级调度执行的;
- 具有最高优先级的线程总是最先执行;
- 只要具有较高优先级的线程可以运行,较低优先级的线程就不会执行;
- 如果多个具有相同优先级的线程可用,程序将遍历处于该优先级的线程,并为每个线程提供一个固定的时间片来执行;
- 如果具有较高优先级的线程可以运行时,较低优先级的线程就会被抢先,并允许较高优先级的线程再次执行。
//优先级从低到高thread1.Priority = ThreadPriority.Lowest; //0thread1.Priority = ThreadPriority.BelowNormal; //1thread1.Priority = ThreadPriority.Normal; //2thread1.Priority = ThreadPriority.AboveNormal; //3thread1.Priority = ThreadPriority.Highest; //4
3.5 示例代码
using System.Threading;//线程的加入 控制进度条的滚动namespace ScrollBarThread{ public partial class Form1 : Form { public Form1() { InitializeComponent(); CheckForIllegalCrossThreadCalls = false; } Thread thread1; Thread thread2; void Func1() { try { int count = 0; while (true) { progressBar1.PerformStep(); count += progressBar1.Step; Thread.Sleep(500); if (count == 20) { thread2.Join(); } } } catch(ThreadAbortException) { MessageBox.Show("线程1被终止"); } } void Func2() { int count = 0; while (true) { progressBar2.PerformStep(); count += progressBar2.Step; Thread.Sleep(500); if (count == 100) { break; } } } private void Form1_Load(object sender, EventArgs e) { thread1 = new Thread(Func1); thread1.Start(); thread2 = new Thread(Func2); thread2.Start(); } private void button1_Click(object sender, EventArgs e) { if(thread1.IsAlive) { thread1.Abort(); } } private void button2_Click(object sender, EventArgs e) { if(thread2.IsAlive) { thread2.Abort(); } } }}
4. 线程同步
4.1 线程同步机制
- 为了解决多线程资源共享的问题,需要给共享资源上一道锁;
- 线程同步是指并发线程高效、有序地访问共享资源所采用的技术。
- 同步是指某一时刻只有一个线程可以访问资源,只有当资源所有者自动放弃了代码或资源的所有权时,其他线程才可以使用这些资源。
4.2 实现线程同步的3种方法
4.2.1 lock
- lock关键字可以确保代码块完成运行,而不会被其他线程中断,它是通过在代码块运行期间为给定对象获取互斥锁来实现的。
4.2.2 Monitor
- Monitor类提供了同步对象的访问机制,它通过向当个线程授予对象锁来控制对象的访问,对象锁提供限制访问代码块(通常称为临界区)的能力。
- 当一个线程拥有对象锁时,其他任何线程都不能获取该锁。
Monitor类的主要功能
- 根据需要与某个对象关联;
- 它是未绑定的,也就是说可以直接从任何上下文调用它;
- 不能创建Monitor类的实例。
Monitor常用方法
- Enter:在指定对象上获取排他锁。
- Exit:释放指定对象上的排他锁。
- Pulse:通知等待队列中的线程锁定对象状态的更改。
- PulseAll:通知所有的等待线程对象状态的修改。
- TryEnter:试图获取指定对象的排他锁。
- Wait:释放对象上的锁并阻塞当前线程,直到它重新获取该锁。
4.2.3 Mutex
- Mutex类是同步基元,它只向一个线程授予对共享资源的独占访问权。
- 如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
- Mutex与Monitor类似,它防止多个线程在某一实践同时执行某个代码块,然而与监视器不同的是,Mutex类可以用来跨进程的线程同步。
Mutex类的常用方法:
- Close:在派生类中被重写时,释放由当前WaitHaldle持有的所有资源。
- Release:释放Mutex一次。
- WaitAll:等待指定数组中的所有元素都收到信号。
- WaitAny:等待指定数组中的任一元素收到信号。
- WaitOne:当在派生类中重写时,阻塞当前线程,直到当前的WaitHandle收到信号。
4.3 示例代码
说明
- 尽管Mutex类可以用于进程内的线程同步,但是使用Monitor更为可取,因为Monitor监视器是专门为.NET Framework而设计的,因而它可以更好地利用资源。相比之下,Mutex类是Win32构造的包装。尽管Mutex类比监视器更为强大,但是相对于Monitor类,它所需的互操作转换更消耗计算资源。
using System.Threading;namespace ThreadLock{ class Program { int num = 10;//票数 void Ticket() { while(true) { //1. lock(this) //锁定代码块 //2. Monitor.Enter(this);//锁定代码块 //3. Mutex mutex = new Mutex();//创建对象 没有起作用,不知道为什么 // mutex.WaitOne(); //阻塞当前线程 { if(num>0) { Thread.Sleep(100); Console.WriteLine(Thread.CurrentThread.Name + "------票数" + num--); } } //2. Monitor.Exit(this);//解锁代码块 //3. mutex.ReleaseMutex(); //释放Mutex对象 } } static void Main(string[] args) { Program p = new Program(); Thread t1 = new Thread(p.Ticket); Thread t2 = new Thread(p.Ticket); Thread t3 = new Thread(p.Ticket); Thread t4 = new Thread(p.Ticket); t1.Name = "线程1"; t2.Name = "线程2"; t3.Name = "线程3"; t4.Name = "线程4"; t1.Start(); t2.Start(); t3.Start(); t4.Start(); Console.ReadKey(); } }}
5. 过时的方法和线程池
5.1 过时的方法
- 由于Suspend方法和Resume方法很容易造成死锁,目前函数已经废弃不用。
5.2 线程池
- 每创建一个线程,在带来便利的同时,也会带来某些不确定的负面因素,比如管理成本、未知Bug等,如果程序中需要创建大量短生命周期的线程,则应该使用线程池。
- 在C#中提供了创建线程池的ThreadPool类,该类提供了一些静态方法,用于发送工作项、处理异步I/O、代表其他线程等待以及处理计时器等。
- 例如,GetMaxThreads方法可以检索同时处于活动状态的线程池请求的数目;GetMinThreads方法可以检索线程池在新请求预测中维护的空闲线程数等。
6. 相关代码下载地址:
链接:https://pan.baidu.com/s/1iaNrRat3Eo8R-7BEwj1MRw
提取码:fzvj发表评论
最新留言
留言是一种美德,欢迎回访!
[***.207.175.100]2025年04月06日 20时11分40秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
IP-Guard回收客户端加密授权,已经加密的文档如何解密
2021-05-10
a*算法伪代码及实现
2021-05-10
第十一节 IO编程
2021-05-10
十八、flask之g对象
2021-05-10
GIT学习笔记
2021-05-10
Linux系统调用过程
2021-05-10
stm32 uv5打开uv4工程错误
2021-05-10
mysql怎么终止一个事务_MySql 中游标,事务,终止存储过程方法总结
2021-05-10
app:processDevDebugResources
2021-05-10
最基础的urllib.request.urlopen()基本使用
2021-05-10
C# 异常
2021-05-10
strlen sizeof 快
2021-05-10
【Java-27】Java常见错误记录
2021-05-10
andriod 开发错误记录
2021-05-10
生成树协议(二)
2021-05-10
创建一个简单的SpingBoot项目,并且部署到linux上运行
2021-05-10
mysql8.0及以上在my.cnf设置sql_mode之后mysql无法启动
2021-05-10
C语言编译错误列表
2021-05-10
万倍币传说不再,价值回归
2021-05-10