当前位置: 首页 > news >正文

多线程编程

    一句话,多线程就是在做一件事的同时也可以做其他事情,就像人可以边走边说话一样。
     讨论多线程之前先了解进程和线程的概念。
进程

     进程是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元。它是60年代初首先由麻省理工学院的MULTICS系统和IBM公司的CTSS/360系统引入的。
  进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。 

线程
      线程(thread)是"进程"中某个单一顺序的控制流。也被称为轻量进程(lightweight processes),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,那就是程序本身。
  线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

举一个简单的例子来理解进程和线程,人的动作这就是一个进程,而人说话,摆手,眨眼等就是一系列的线程。

进程与程序的关系

  •   程序是指令的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
  •   程序可以作为一种软件资料长期存在,而进程是有一定生命期的。程序是永久的,进程是暂时的。
  •   进程更能真实地描述并发,而程序不能。
  •   进程是由进程控制块、程序段、数据段三部分组成。
  •   进程具有创建其他进程的功能,而程序没有。
  •   同一程序同时运行于若干个数据集合上,它将属于若干个不同的进程。也就是说同一程序可以对应多个进程。
  •   在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单元都是进程。

进程与线程的关系

      通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。因而近年来推出的通用操作系统都引入了线程,以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标。


线程与进程的区别

  •   地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
  •   通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
  •   调度和切换:线程上下文切换比进程上下文切换要快得多。
  •   在多线程OS中,进程不是一个可执行的实体。


C#中的多线程开发

      在计算机编程中,一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。最开始的时候,那些掌握机器低级语言的程序员编写一些“中断服务例程”(比如汇编语言中的int 21h等),主进程的暂停是通过硬件级的中断实现的。尽管这是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。中断对那些实时性很强的任务来说是很有必要的。但对于其他许多问题,只要求将问题划分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求。

      在.net中编写的程序将被自动分配一个线程。.net的运行时环境由Main()方法来启动应用程序,而且.net的编程语言有自动的垃圾收集功能,这个垃圾收集功能发生在另外一个线程里,所有这些当然是在后台发生的,我们不去理会这些动作。但是大多数情况下我们需要设置自己的线程来调用工作,在.net基础类库中的System.Threading命名空间中,就存在线程编程的类。


线程操作
System.Threading.Thread类是创建并操作线程,设置其优先级并获得其状态最为常用的类。

常用构造函数

构造函数说明
Thread(ParameterizedThreadStart)初始化 Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托。
Thread(ThreadStart)初始化 Thread 类的新实例。





常用属性

属性说明
CurrentThread获取当前正在运行的线程。
IsAlive获取一个值,该值指示当前线程的执行状态。
IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。
Name获取或设置线程的名称。
Priority获取或设置一个值,该值指示线程的调度优先级。
ThreadState获取一个值,该值包含当前线程的状态。









常用方法

方法说明
Abort

 在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。

GetType

获取当前实例的 Type。
Resume继续已挂起的线程。
Sleep将当前线程挂起指定的时间或时间段。
Start导致操作系统将当前实例的状态更改为 ThreadState.Running。
Suspend挂起线程,或者如果线程已挂起,则不起作用。











  • 创建线程
    • 当然这就是实例化一个Thread类的对象,它的构造函数有一个参数ThreadStart类型的参数,这是一个委派用于传递线程的入口方法,而创建ThreadStart对象需要以一个静态方法或者是实例方法为参数。
  • 启动线程
    • 启动线程只需要用Start()方法就行。
  • 休眠线程
    • 休眠线程就是让当前线程进入一定的休眠期,时间一到线程将继续执行,当然是调用Sleep方法,它有两个参数,毫秒ms,或者是TimeSpan(时间段)。
  • 挂起线程
    • 线程的挂起就是暂停线程,如果不在启动线程,它将永远暂停,但前提是只有当前线程是运行的才可以,即判断ThreadState,之后调用Suspend方法。
  • 继续线程
    • 已经挂起的线程使用Thread的Resume方法继续进行,于是一般会先判断状态是不是挂起的。
  • 终止线程
    • 首先确定线程是不是IsAlive,即线程是不是活动着的,如果是活动着的,就调用Abort方法来终止线程。


 示例1:以winform窗体程序为例,在移动窗体的同时不能有其他代码执行,因为窗体是由UI线程创建,在执行UI中的代码时如果要进行其他代码段,那么用多线程解决。

 代码:

  1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Threading;
10
11 namespace 多线程实例
12 {
13 public partial class 多线程实例 : Form
14 {
15 public 多线程实例()
16 {
17 InitializeComponent();
18 TextBox.CheckForIllegalCrossThreadCalls = false;//由于textbox控件是由UI线程创建的,所以其他线程想要访问则系统会进行捕获对错误线程的调用,在这里关闭自动捕获这种功能。
19 }
20
21 //单线程缺点,即不能移动窗体
22 private void btnSingleThread_Click(object sender, EventArgs e)
23 {
24 this.CountTime();
25 }
26
27 //一个示例方法
28 private void ChangeValue()
29 {
30 for (int i = 0; i < 1000; i++)
31 {
32 int a = int.Parse(this.txtValue.Text.Trim());
33 Console.WriteLine(Thread.CurrentThread .Name +",a="+a);
34 a++;
35 this.txtValue.Text = a.ToString();
36 }
37 MessageBox.Show("循环完毕~~~~:)");
38
39 }
40
41 //多线程方法重入问题,会发现结果不是预期的1000
42 private void btnProblem_Click(object sender, EventArgs e)
43 {
44 ThreadStart ts=new ThreadStart (this.ChangeValue);
45 Thread myThread = new Thread(ts);
46 myThread.Name = "t1";
47 myThread.IsBackground = true;
48 myThread.Start();
49
50 ThreadStart ts2 = new ThreadStart(this.ChangeValue);
51 Thread myThread2 = new Thread(ts2);
52 myThread2.Name = "t2";
53 myThread2.IsBackground = true;
54 myThread2.Start();
55 }
56 //多线程解决
57 private void btnMulThread_Click(object sender, EventArgs e)
58 {
59 ThreadStart ts = new ThreadStart(this.CountTime );
60 Thread myThread = new Thread(ts);
61 myThread.IsBackground = true;
62 myThread.Start();
63 }
64 //一个不带参数的方法
65 private void CountTime()
66 {
67 DateTime beginTime = DateTime.Now;
68
69 for (int i = 0; i < 999999999; i++)
70 { }
71
72 TimeSpan ts =beginTime.Subtract(DateTime.Now );
73 double val = Convert.ToDouble (ts.TotalMilliseconds );
74 MessageBox.Show("循环完毕~~~~:),用时"+ Math.Abs(val)+"毫秒");
75
76 }
77 //带参数的线程
78 private void btnMulThreadWithParams_Click(object sender, EventArgs e)
79 {
80 ParameterizedThreadStart pts = new ParameterizedThreadStart(this.ShowName );
81 Thread myThread = new Thread(pts);
82 myThread.IsBackground = true;
83 myThread.Start(this.txtValue.Text.Trim ());
84 }
85
86 //带参数的方法,如果要用在线程中实现,则必须是object类型
87 private void ShowName(object name)
88 {
89 MessageBox.Show("name="+name.ToString());
90 }
91
92 //带多参数的多线程
93 private void btnMulThreadWithMulParams_Click(object sender, EventArgs e)
94 {
95 ParameterizedThreadStart pts = new ParameterizedThreadStart(this.ShowName2);
96 Thread myThread = new Thread(pts);
97 myThread.IsBackground = true;
98 myThread.Start(new List<string>() { "123","456"});
99 }
100
101 //带参数的方法,如果要用在线程中实现,则必须是object类型
102 private void ShowName2(object name)
103 {
104 List <string> list=name as List<string>;
105 if(list !=null)
106 {
107 foreach (string s in list )
108 {
109 MessageBox.Show("name=" +s);
110 }
111 }
112 }
113 }
114 }

 

设计界面

示例2:简单的线程操作

代码:

 1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Threading;
10
11 namespace 多线程实例
12 {
13 public partial class 线程操作方法 : Form
14 {
15 public 线程操作方法()
16 {
17 InitializeComponent();
18 Label.CheckForIllegalCrossThreadCalls = false;//关闭线程的检测
19 }
20
21 Thread myThread = null;//声明一个线程
22
23 /// <summary>
24 /// 线程调用的方法,显示当前时间
25 /// </summary>
26 private void ShowTime()
27 {
28 while (true)
29 {
30 this.lblShow.Text = DateTime.Now.ToString();
31 }
32 }
33
34 //页面初始化,显示当前时间
35 private void 线程操作方法_Load(object sender, EventArgs e)
36 {
37 this.lblShow.Text += DateTime.Now.ToString();
38 }
39
40 //创建线程
41 private void ThreadCreate_Click(object sender, EventArgs e)
42 {
43 ThreadStart ts = new ThreadStart(ShowTime);
44 myThread = new Thread(ts);//初始化线程
45 MessageBox.Show("创建线程成功");
46 //myThread.IsBackground = true;//设置为后台线程
47 }
48 //启动线程
49 private void ThreadStart_Click(object sender, EventArgs e)
50 {
51 myThread.Start();
52 }
53 //休眠线程
54 private void ThreadSleep_Click(object sender, EventArgs e)
55 {
56 Thread.Sleep(new TimeSpan(0,0,0,3,0));//休眠3秒
57 }
58 //挂起线程
59 private void ThreadSuspend_Click(object sender, EventArgs e)
60 {
61 if (myThread.ThreadState==ThreadState.Running)//判断线程是否是运行的
62 {
63 myThread.Suspend();//挂起
64 }
65 }
66 //继续线程
67 private void ThreadResume_Click(object sender, EventArgs e)
68 {
69 if (myThread.ThreadState==ThreadState.Suspended)//判断线程是否是挂起的
70 {
71 myThread.Resume();//继续线程
72 }
73 }
74 //终止线程
75 private void ThreadAbort_Click(object sender, EventArgs e)
76 {
77 if (myThread.IsAlive)//判断线程是否是活动的
78 {
79 myThread.Abort();//终止线程
80 }
81 }
82 }
83 }

界面:

线程同步

      在包含多个线程的应用程序中,线程间有时会共享存储空间,当两个或多个线程同时访问统一资源时,必然会冲突。如果两个线程分别执行读、写操作,那么结果将不是预期的结果。因此我们要保证线程的一个访问次序,即按照一定的规则使某一个线程先访问资源,另一个线程后访问。
      在C#.Net中,提供了多种线程同步的方法,如加锁(Lock)、监视器(Monitor)、互斥体(Mutex)。

 

  • 加锁(Lock)
  • 实现多线程同步的最简单的方法就是加锁,使用lock语句就行,它可以把一段代码定义为互斥段,在一个时刻内只允许一个线程进入执行,其他线程进行等待。
  • 格式:lock (expression) statement_block
  • expression代表要加锁的对象,必须是一引用类型。一般的,如果要保护一个类的实例成员,可以使用this,如果要保护一个静态成员,或者要保护的内容位于一个静态方法中,可以使用类名,格式为:lock(typeof(类名))。
  • statement_block代表共享资源。


  • 监视器(Monitor)
  • Monitor的功能和lock有些类似,但是比lock更加强大与灵活。
  • 位于System.Threading命名空间中的Monitor类是一个静态类,当然它所有的方法都是静态方法。它通过Enter方法向单个线程授予锁定对象的“钥匙”来控制锁定对象,该方法提供限制访问代码块(通常称为临界区,由Enter方法标记临界区的开头,Exit方法来标记临界区的结尾)的功能。

 

  • 互斥体(Mutex)
  • 互斥体是通过只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。在System.Threading命名空间中的Mutex类代表了互斥体,它继承于WaitHandle类,该类代表了所有的同步对象。

示例3:这个例子来源于《C#网络编程技术教程》,是一个模拟吃苹果的例子,要求:一家有三个孩子,爸爸妈妈不断削平果往盘子里放,老大、老二、老三不断从盘子里拿苹果吃。盘子只能放5个苹果,并且爸爸妈妈不能同时往盘子里放苹果,妈妈具有优先权。三个孩子取苹果吃的时候盘子不能是空的,三人不能同时取,老三优先权最高,老大最低,老大吃的最快,取得频率最高,老二次之。

代码:

  1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6
7 namespace 多线程实例
8 {
9 class EatAppleSmp
10 {
11 public EatAppleSmp()
12 {
13 Thread th_mother, th_father, th_young, th_middle, th_old;
14 Dish dish = new Dish(this, 30);
15 //建立线程
16 Productor mother = new Productor("妈妈", dish);
17 Productor father = new Productor("爸爸", dish);
18 Consumer old = new Consumer("老大",dish,1000);
19 Consumer middle = new Consumer("老二",dish,1200);
20 Consumer young = new Consumer("老三",dish,1500);
21 th_mother = new Thread(new ThreadStart(mother.run));
22 th_father = new Thread(new ThreadStart(father.run));
23 th_old = new Thread(new ThreadStart(old.run));
24 th_mother = new Thread(new ThreadStart(mother.run));
25 th_middle = new Thread(new ThreadStart(middle.run));
26 th_young = new Thread(new ThreadStart(young.run));
27 //设置优先级
28 th_mother.Priority = ThreadPriority.Highest;
29 th_father.Priority = ThreadPriority.Normal;
30 th_old.Priority = ThreadPriority.Lowest;
31 th_middle.Priority = ThreadPriority.Normal;
32 th_young.Priority = ThreadPriority.Highest;
33 th_mother.Start();
34 th_father.Start();
35 th_old.Start();
36 th_middle.Start();
37 th_young.Start();
38 }
39
40 static void Main(string[] args)
41 {
42 EatAppleSmp mainStart = new EatAppleSmp();
43 }
44 }
45 class Dish
46 {
47 int f = 5;//盘子中最多五个苹果
48 EatAppleSmp oEAP;
49 int EnableNum;//可放苹果总数
50 int n = 0;
51
52 public Dish(EatAppleSmp oEAP, int EnableNum)
53 {
54 this.oEAP = oEAP;
55 this.EnableNum = EnableNum;
56 }
57 public void put(string name)
58 {
59 lock (this)//同步控制放苹果
60 {
61 while (f == 0)//苹果已满时,线程等待
62 {
63 try
64 {
65 Console.WriteLine(name+"正在等地放入苹果");
66 Monitor.Wait(this);
67 }
68 catch (ThreadInterruptedException)
69 {
70
71 throw;
72 }
73 }
74 f = f - 1;//削完一个苹果放一个
75 n = n + 1;
76 Console.WriteLine(name+"放入一个苹果");
77 Monitor.PulseAll(this);
78 if (n > this.EnableNum)
79 {
80 Thread.CurrentThread.Abort();
81 }
82 }
83 }
84 public void get(string name)
85 {
86 lock (this)//同步控制取苹果
87 {
88 while (f == 5)
89 {
90 try
91 {
92 Console.WriteLine(name+"等待取苹果");
93 Monitor.Wait(this);
94 }
95 catch (ThreadInterruptedException)
96 {
97
98 throw;
99 }
100 }
101 f = f + 1;
102 Console.WriteLine(name+"取苹果吃...");
103 Monitor.PulseAll(this);
104 }
105 }
106 }
107
108 class Productor
109 {
110 private Dish dish;
111 private string name;
112
113 public Productor(string name, Dish dish)
114 {
115 this.dish = dish;
116 this.name = name;
117 }
118 public void run()
119 {
120 while (true)
121 {
122 dish.put(name);
123 try
124 {
125 Thread.Sleep(600);//削苹果时间
126 }
127 catch (ThreadInterruptedException )
128 {
129
130 throw;
131 }
132 }
133 }
134 }
135
136 class Consumer
137 {
138 private string name;
139 private Dish dish;
140 private int timelong;
141
142 public Consumer(string name, Dish dish, int timelong)
143 {
144 this.name = name;
145 this.dish = dish;
146 this.timelong = timelong;
147 }
148 public void run()
149 {
150 while (true)
151 {
152 dish.get(name);
153 try
154 {
155 Thread.Sleep(600);//吃苹果时间
156 }
157 catch (ThreadInterruptedException )
158 {
159
160 throw;
161 }
162 }
163 }
164
165 }
166 }

 


转载于:https://www.cnblogs.com/waitingsky/archive/2012/02/28/MultipleThread.html

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 分享20佳极具创意的网站导航菜单设计案例
  • Linux多进程_消息通信_设计思想交流(转)
  • 第一章:第二课 选择器-结构性伪类选择器[五]
  • nyoj 16 - 矩形嵌套
  • Excel导入导出
  • 基础常用JS
  • 记载公共语言运行库
  • 一篇关于WPF“样式”介绍的好文
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • TCP编程
  • 在线客服 qq,msn,skype, outlook 链接
  • 惠普前CEO卡莉讲演:千万不要贩卖你的灵魂(转载)
  • .net中应用SQL缓存(实例使用)
  • js函数集合大全
  • 用vs命令提示符来使用 Installutil.exe来安装和卸载Windows服务
  • 【附node操作实例】redis简明入门系列—字符串类型
  • axios 和 cookie 的那些事
  • echarts花样作死的坑
  • ES6语法详解(一)
  • github从入门到放弃(1)
  • httpie使用详解
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Linux gpio口使用方法
  • mysql常用命令汇总
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 飞驰在Mesos的涡轮引擎上
  • 关于字符编码你应该知道的事情
  • 开源地图数据可视化库——mapnik
  • 面试总结JavaScript篇
  • # Panda3d 碰撞检测系统介绍
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #QT项目实战(天气预报)
  • #Z0458. 树的中心2
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (ros//EnvironmentVariables)ros环境变量
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (亲测有效)推荐2024最新的免费漫画软件app,无广告,聚合全网资源!
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (转)Unity3DUnity3D在android下调试
  • (转)用.Net的File控件上传文件的解决方案
  • *Django中的Ajax 纯js的书写样式1
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .Net 知识杂记
  • .NET/C# 使窗口永不获得焦点
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • [14]内置对象
  • [2]十道算法题【Java实现】
  • [AI Embedchain] 开始使用 - 全栈
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心
  • [BZOJ1008][HNOI2008]越狱
  • [C#]使用DlibDotNet人脸检测人脸68特征点识别人脸5特征点识别人脸对齐人脸比对FaceMesh