C#中的多线程编程
在Visul C#中System.Threading 命名空间提供一些使得可以进行多线程编程的类和接口,其中线程的创建有以下三种方法:Thread、ThreadPool、Timer。下面我就它们的使用方法逐个作一简单介绍。
1. Thread
这也许是最复杂的方法,但它提供了对线程的各种灵活控制。首先你必须使用它的构造函数创建一个线程实例,它的参数比较简单,只有一个ThreadStart 委托:
[C#]
public Thread(ThreadStart start);
然后调用Start()启动它,当然你可以利用它的Priority属性来设置或获得它的运行优先级(enum ThreadPriority: Normal、 Lowest、 Highest、 BelowNormal、 AboveNormal)。见下例:它首先生成了两个线程实例t1和t2,然后分别设置它们的优先级,接着启动两线程(两线程基本一样,只不过它们输出不一样,t1为“1”,t2为“2”,根据它们各自输出字符个数比可大致看出它们占用CPU时间之比,这也反映出了它们各自的优先级)。
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Thread1));
Thread t2 = new Thread(new ThreadStart(Thread2));
t1.Priority = ThreadPriority.BelowNormal ;
t2.Priority = ThreadPriority.Lowest ;
t1.Start();
t2.Start();
}
public static void Thread1()
{
for (int i = 1; i < 1000; i++)
{//每运行一个循环就写一个“1”
dosth();
Console.Write("1");
}
}
public static void Thread2()
{
for (int i = 0; i < 1000; i++)
{//每运行一个循环就写一个“2”
dosth();
Console.Write("2");
}
}
public static void dosth()
{//用来模拟复杂运算
for (int j = 0; j < 10000000; j++)
{
int a=15;
a = a*a*a*a;
}
}
以上程序运行结果为:
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
从以上结果我们可以看出,t1线程所占用CPU的时间远比t2的多,这是因为t1的优先级比t2的高,若我们把t1和t2的优先级都设为Normal,那结果是如何?它们所占用的CPU时间会一样吗?是的,正如你所料,见下图:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
从上例我们可看出,它的构造类似于win32的工作线程,但更加简单,只需把线程要调用的函数作为委托,然后把委托作为参数构造线程实例即可。当调用Start()启动后,便会调用相应的函数,从那函数第一行开始执行。
接下来我们结合线程的ThreadState属性来了解线程的控制。ThreadState是一个枚举类型,它反映的是线程所处的状态。当一个Thread实例刚创建时,它的ThreadState是Unstarted;当此线程被调用Start()启动之后,它的ThreadState是 Running; 在此线程启动之后,如果想让它暂停(阻塞),可以调用Thread.Sleep() 方法,它有两个重载方法(Sleep(int )、Sleep(Timespan )),只不过是表示时间量的格式不同而已,当在某线程内调用此函数时,它表示此线程将阻塞一段时间(时间是由传递给 Sleep 的毫秒数或Timespan决定的,但若参数为0则表示挂起此线程以使其它线程能够执行,指定 Infinite 以无限期阻塞线程),此时它的ThreadState将变为WaitSleepJoin,另外值得注意一点的是Sleep()函数被定义为了static?! 这也意味着它不能和某个线程实例结合起来用,也即不存在类似于t1.Sleep(10)的调用!正是如此,Sleep()函数只能由需“Sleep”的线程自己调用,不允许其它线程调用,正如when to Sleep是个人私事不能由它人决定。但是当某线程处于WaitSleepJoin状态而又不得不唤醒它时,可使用Thread.Interrupt 方法 ,它将在线程上引发ThreadInterruptedException,下面我们先看一个例子(注意Sleep的调用方法):
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Thread1));
t1.Start();
t1.Interrupt ();
E.WaitOne ();
t1.Interrupt ();
t1.Join();
Console.WriteLine(“t1 is end”);
}
static AutoResetEvent E = new AutoResetEvent(false);
public static void Thread1()
{
try
{//从参数可看出将导致休眠
Thread.Sleep(Timeout.Infinite);
}
catch(System.Threading.ThreadInterruptedException e)
{//中断处理程序
Console.WriteLine (" 1st interrupt");
}
E.Set ();
try
{// 休眠
Thread.Sleep(Timeout.Infinite );
}
catch(System.Threading.ThreadInterruptedException e)
{
Console.WriteLine (" 2nd interrupt");
}//暂停10秒
Thread.Sleep (10000);
}
运行结果为: 1st interrupt
2nd interrupt
(10s后)t1 is end
从上例我们可以看出Thread.Interrupt方法可以把程序从某个阻塞(WaitSleepJoin)状态唤醒进入对应的中断处理程序,然后继续往下执行(它的ThreadState也变为Running),此函数的使用必须注意以下几点:
1 .此方法不仅可唤醒由Sleep导致的阻塞,而且对一切可导致线程进入WaitSleepJoin状态的方法(如Wait和Join)都有效。如上例所示, 使用时要把导致线程阻塞的方法放入try块内, 并把相应的中断处理程序放入catch块内。
2 .对某一线程调用Interrupt, 如它正处于WaitSleepJoin状态, 则进入相应的中断处理程序执行, 若此时它不处于WaitSleepJoin状态, 则它后来进入此状态时, 将被立即中断。若在中断前调用几次Interrupt, 只有第一次调用有效, 这正是上例我用同步的原因, 这样才能确保第二次调用Interrupt在第一个中断后调用,否则的话可能导致第二次调用无效(若它在第一个中断前调用)。你可以把同步去掉试试,其结果很可能是: 1st interrupt
上例还用了另外两个使线程进入WaitSleepJoin状态的方法:利用同步对象和Thread.Join方法。Join方法的使用比较简单,它表示在调用此方法的当前线程阻塞直至另一线程(此例中是t1)终止或者经过了指定的时间为止(若它还带了时间量参数),当两个条件(若有)任一出现,它立即结束WaitSleepJoin状态进入Running状态(可根据.Join方法的返回值判断为何种条件,为true,则是线程终止;false则是时间到)。
线程的暂停还可用Thread.Suspend方法,当某线程处于Running状态时对它调用Suspend方法,它将进入SuspendRequested状态,但它并不会被立即挂起,直到线程到达安全点之后它才可以将该线程挂起,此时它将进入Suspended状态。如对一个已处于Suspended的线程调用则无效,要恢复运行只需调用Thread.Resume即可。
最后我们谈的是线程的销毁,我们可以对需销毁的线程调用Abort方法,它会在此线程上引发ThreadAbortException。我们可把线程内的一些代码放入try块内,并把相应处理代码放入相应的catch块内,当线程正执行try块内代码时如被调用Abort,它便会跳入相应的catch块内执行,执行完catch快内的代码后它将终止(若catch块内执行了ResetAbort则不同了:它将取消当前Abort请求,继续向下执行。所以如要确保某线程终止的最好用Join,如上例)。
2. ThreadPool
线程池(ThreadPool)是一种相对较简单的方法,它适应于一些需要多个线程而又较短任务(如一些常处于阻塞状态的线程) ,它的缺点是对创建的线程不能加以控制,也不能设置其优先级。由于每个进程只有一个线程池,当然每个应用程序域也只有一个线程池(对线),所以你将发现ThreadPool类的成员函数都为static! 当你首次调用ThreadPool.QueueUserWorkItem、ThreadPool.RegisterWaitForSingleObject等,便会创建线程池实例。下面我就线程池当中的两函数作一介绍:
[C#]
public static bool QueueUserWorkItem( //调用成功则返回true
WaitCallback callBack,//要创建的线程调用的委托
object state //传递给委托的参数
)//它的另一个重载函数类似,只是委托不带参数而已
此函数的作用是把要创建的线程排队到线程池,当线程池的可用线程数不为零时(线程池有创建线程数的限制,缺身值为25),便创建此线程,否则就排队到线程池等到它有可用的线程时才创建。
[C#]
public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,// 要注册的 WaitHandle
WaitOrTimerCallback callBack,// 线程调用的委托
object state,//传递给委托的参数
int TimeOut,//超时,单位为毫秒,
bool executeOnlyOnce file://是/否只执行一次
);
public delegate void WaitOrTimerCallback(
object state,//也即传递给委托的参数
bool timedOut//true表示由于超时调用,反之则因为waitObject
);
此函数的作用是创建一个等待线程,一旦调用此函数便创建此线程,在参数waitObject变为终止状态或所设定的时间TimeOut到了之前,它都处于“阻塞”状态,值得注意的一点是此“阻塞”与Thread的WaitSleepJoin状态有很大的不同:当某Thread处于WaitSleepJoin状态时CPU会定期的唤醒它以轮询更新状态信息,然后再次进入WaitSleepJoin状态,线程的切换可是很费资源的;而用此函数创建的线程则不同,在触发它运行之前,CPU不会切换到此线程,它既不占用CPU的时间又不浪费线程切换时间,但CPU又如何知道何时运行它?实际上线程池会生成一些辅助线程用来监视这些触发条件,一旦达到条件便启动相应的线程,当然这些辅助线程本身也占用时间,但是如果你需创建较多的等待线程时,使用线程池的优势就越加明显。见下例:
static AutoResetEvent ev=new AutoResetEvent(false);
public static int Main(string[] args)
{ ThreadPool.RegisterWaitForSingleObject(
ev,
new WaitOrTimerCallback(WaitThreadFunc),
4,
2000,
false//表示每次完成等待操作后都重置计时器,直到注销等待
);
ThreadPool.QueueUserWorkItem (new WaitCallback (ThreadFunc),8);
Thread.Sleep (10000);
return 0;
}
public static void ThreadFunc(object b)
{ Console.WriteLine ("the object is {0}",b);
for(int i=0;i<2;i++)
{ Thread.Sleep (1000);
ev.Set();
}
}
public static void WaitThreadFunc(object b,bool t)
{ Console.WriteLine ("the object is {0},t is {1}",b,t);
}
其运行结果为:
the object is 8
the object is 4,t is False
the object is 4,t is False
the object is 4,t is True
the object is 4,t is True
the object is 4,t is True
从以上结果我们可以看出线程ThreadFunc运行了1次,而WaitThreadFunc运行了5次。我们可以从WaitOrTimerCallback中的bool t参数判断启动此线程的原因:t为false,则表示由于waitObject,否则则是由于超时。另外我们也可以通过object b向线程传递一些参数。
3. Timer
它适用于需周期性调用的方法,它不在创建计时器的线程中运行,它在由系统自动分配的单独线程中运行。这和Win32中的SetTimer方法类似。它的构造为:
[C#]
public Timer(
TimerCallback callback,//所需调用的方法
object state,//传递给callback的参数
int dueTime,//多久后开始调用callback
int period//调用此方法的时间间隔
);// 如果 dueTime 为0,则 callback 立即执行它的首次调用。如果 dueTime 为 Infinite,则 callback 不调用它的方法。计时器被禁用,但使用 Change 方法可以重新启用它。如果 period 为0或 Infinite,并且 dueTime 不为 Infinite,则 callback 调用它的方法一次。计时器的定期行为被禁用,但使用 Change 方法可以重新启用它。如果 period 为零 (0) 或 Infinite,并且 dueTime 不为 Infinite,则 callback 调用它的方法一次。计时器的定期行为被禁用,但使用 Change 方法可以重新启用它。
在创建计时器之后若想改变它的period和dueTime,我们可以通过调用Timer的Change方法来改变:
[C#]
public bool Change(
int dueTime,
int period
);//显然所改变的两个参数对应于Timer中的两参数
见下例:public static int Main(string[] args)
{ Console.WriteLine ("period is 1000");
Timer tm=new Timer (new TimerCallback (TimerCall),3,1000,1000);
Thread.Sleep (2000);
Console.WriteLine ("period is 500");
tm.Change (0,800);
Thread.Sleep (3000);
return 0;
}
public static void TimerCall(object b)
{
Console.WriteLine ("timercallback; b is {0}",b);
}
其运行结果为:
period is 1000
timercallback;b is 3
timercallback;b is 3
period is 500
timercallback;b is 3
timercallback;b is 3
timercallback;b is 3
timercallback;b is 3
总结
从以上的简单介绍,我们可以看出它们各自使用的场合:Thread适用于那些需对线程进行复杂控制的场合;ThreadPool适应于一些需要多个线程而又较短任务(如一些常处于阻塞状态的线程);Timer则适用于那些需周期性调用的方法。只要我们了解了它们的使用特点,我们就可以很好的选择合适的方法。
相关文章:

关于机器学习,不可不知的15个概念
作者:布奇昆托(Butch Quinto)来源:大数据DT(ID:hzdashuju)有监督学习有监督学习是利用训练数据集进行预测的机器学习任务。有监督学习可以分为分类和回归。回归用于预测“价格”“温度”或“…

常用API(Object、String、StringBuffer、用户登陆注册)
常用API 今日内容介绍 u Object u String u StringBuilder 第1章 Java的API及Object类 在以前的学习过程中,我们都在学习对象基本特征、对象的使用以及对象的关系。接下来我们开始使用对象做事情,那么在使用对象做事情之前,我们要学习一些API…

WMI列出磁盘配额
using System; using System.Management; namespace DiskQuota { /// <summary> /// Class1 的摘要说明。 /// </summary> class Class1 { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main(string[] args) { try…

VS2013 编译使用 stlport
1. 下载stlport. 下载地址:http://sourceforge.net/projects/stlport/ 2. 解压到一个目录下面, 我的是解压到D:\project_kuku\program\library\STLport-5.2.1 3. 打开vs2013的命令行工具, 在目录:C:\Program Files\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts\ 下…

盛夏海边,用Python分析青岛哪些景点性价比高
作者 | 志斌来源 | 志斌的python笔记头图 | 付费下载于 IC Photo在经过几年的热潮之后,人工智能AI算法已经在各行各业广泛使用了。例如在工业制造中,利用人工智能监测仪器仪表、人员的违规与高危行为;利用图形AI算法进行材料质检;…

EnterLib PIAB又一个BUG?
在《这是EnterLib PIAB的BUG吗?》一文中我们讨论了PIAB关于抽象基类的BUG,今天又发现了一个新的问题。问题的起因源于《IoCAOP的简单实现》这篇文章,因为文中给出的解决方案仅仅支持构造器注入(Constructor Injection)…

【SQL Server】系统学习之一:表表达式
本节讨论的相关内容包括:视图、派生表、CTE、内联表值函数 场景:如果要查询一组数据(例如聚合数据,也就是几个表聚合在一起的数据),这些数据并未在数据库中以表的形式存在。 1、视图:通常用来分…

C语言中字符数组和字符串指针分析
写下面的测试程序:#include <stdio.h> int main(int argc, char *argv[]){char day[15] "abcdefghijklmn"; char* strTmp "opqrstuvwxyz";printf("&day is %x\n",&day); printf("&day[0] is %x\n",&…

这篇论文,透露谷歌团队构想的“未来搜索”
作者 | 青苹果来源 | 数据实战派头图 | 付费下载于 IC Photo传统的信息检索(IR, Information Retrieval)系统,并不直接回应信息需求,而仅提供对内容的参考。排序(Ranking)是该范式的关键组成部分。这样的检…

selenium+python自动化81-html报告优化(饼图+失败重跑+兼容python23)
优化html报告 为了满足小伙伴的各种变态需求,为了装逼提升逼格,为了让报告更加高大上,测试报告做了以下优化: 测试报告中文显示,优化一些断言失败正文乱码问题新增错误和失败截图,展示到html报告里优化点击…

C#的多线程机制
一.多线程的概念 Windows是一个多任务的系统,如果你使用的是windows 2000及其以上版本,你可以通过任务管理器查看当前系统运行的程序和进程。什么是进程呢?当一个程序开始运行时,它就是一个进程,进程所指包括运行中…

知乎 CTO 李大海:创业公司如何拥抱开源
由计算机学会主办的 CCF C 活动近日走进知乎。知乎合伙人兼 CTO 李大海、搜狗 CEO 王小川、华东师范大学教授王伟在内的企业界、学界技术代表齐聚,带来一场精彩的技术分享盛宴。 围绕「创业公司如何拥抱开源」的分享主题,知乎合伙人兼 CTO 李大海首次披…

如何居中一个浮动元素?
2019独角兽企业重金招聘Python工程师标准>>> 1.<!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>center</title> <style type"text/css"> .p{ position:relative; float:left; left:50…

javascript promise编程
在loop中使用promise: https://stackoverflow.com/questions/17217736/while-loop-with-promises 转载于:https://www.cnblogs.com/kidsitcn/p/7745973.html

bzoj1295
考虑到这道题n,m都很小,我们考虑先穷举起点i 下面我们要做的是找出移走k个障碍后,点i所能到的最大距离 我们可以把这个问题转化为判定性问题 对于一对点i,j,如果他们之间存在一条路径,障碍数(包括起点终点)…

C#程序可将文本文件藏于位图中,也可导出
//使用方法: // BmpSafe.exe /file2bmp (input BMP) (input file to hide) [output file] //BmpSafe.exe /bmp2file (data BMP) [output file] using System; using System.IO; using System.Drawing; public class Bitmap24Writer { protected Bitmap bmp; …

溢价 5 倍欲将 SiFive 收入麾下,英特尔的绝地反击战
作者 | 马超责编 | 张红月出品 | CSDN(ID:CSDNnews)受任于败军之际,奉命于危难之间。近几年硅谷史上的传奇、英特尔几位掌门葛洛夫与欧德宁相继离世,虽然他们早已脱离一线多年,但是他们的离去可能还是让英特…
教你用Vue渐进式搭建聊天室,从JavaScript=TypeScript
前言 VueSocket.io这个轮子已经有很多人造过了,为了不重复造轮子,我将本项目以三阶段实现(大家可以在github中的Releases查看): 纯前端(Vuex)后端前端(JavaScript)后端前…

如何学习linux编程
如果想学习UNIX/LINUX的编程,《APUE》绝对经典的教材,加深一下功底,学习《UNP》的第二卷。这样基本上系统方面的就可以掌握了。如果继续网络编程,建议看《TCP/IP进行网际互连》的第三卷,里面有很多关于应用协议telnet、…

HTML中的form表单有一个关键属性 enctype
HTML中的form表单有一个关键属性 enctype=application/x-www-form-urlencoded 或multipart/form-data。 1、enctype"application/x-www-form-urlencoded"是默认的编码方式,当以这种方式提交数据时,HTTP报文中的内容是: …

赠书 | JavaScript 武力值飙升!用 TensorFlow.js 轻松在浏览器里搞深度学习
近年来,AI 与人类的生活越来越紧密,慢慢变得无处不在。那么提到 AI ,我们会想到什么?小编最先想到的是机器人。早在小学作文中,我就写到 2021 年到处都是机器人,机器人汽车到处飞。结果 2021 年到来&#x…

[译] JWT 与 Spring Cloud 微服务
keyholesoftware.com/2016/06/20/…作者:THOMAS KENDALL译者:oopsguy.com 微服务安全是架构的一个重要部分。具体来说,就是认证和授权模式。 微服务认证和授权处理方式有几种选择,但本文只介绍 JSON Web Token 的使用。 JSON Web …

20步打造最安全的Nginx Web服务器
Nginx是一个轻量级的,高性能的Web服务器以及反向代理和邮箱(IMAP/POP3)代理服务器。它运行在UNIX,GNU/Linux,BSD各种版本,Mac OS X,Solaris和Windows。根据调查统计,6%的网站使用Nginx Web服务器。Nginx是少数能处理C10K问题的服务器之一。跟…

C#创建和调用DLL
一、写在前面 C# 语言是一种简单但功能强大的编程语言,用于编写企业应用程序。 C# 语言从C和 C语言演化而来,在语句、表达式和运算符方面使用了许多 C 功能。 C# 语言在类型安全性、版本转换、事件和垃圾回收等方面进行了相当大的改进和创新。 C# 语言提…

死磕算法!35 篇算法设计实例+6 本必读书打包送你
算法为什么难学?算法在程序中扮演着非常重要的角色,有人将数据结构比喻为程序的骨架,将算法比喻为程序的灵魂,这一点也不为过,正是因为这一点,很多朋友都立志要学好算法,但是我常常看到各种抱怨…

EXCHANGE证书
证书: CA(证书颁发机构)和证书有什么区别? CA:是服务器中的一个服务,主要是用来为计算机(用户)来颁发证书,安装CA的服务器称为证书服务器, 证书:从…

C#2.0模拟List和内置算法
C#中的范型对于很多从C转过来的程序员来说,可以说是一个天大的喜讯。hehe,至少笔者对于这个新特性是充满了敬仰之情。 在C#2.0中,匿名方法、IEnumerable接口和匿名方法的合作,使很多的编程任务变得非常的简单,而且写出…

横扫六大权威榜单后,达摩院开源深度语言模型体系 AliceMind
整理 | AI 科技大本营(ID:rgznai100)自然语言处理(NLP)被誉为 AI 皇冠上的明珠,传统 NLP 模型制作复杂,耗时耗力,且用途单一,难以复用。预训练语言模型是 NLP 领域的研究热点之一&am…

WP8:Unity3D之间的值传递
原地址:http://www.cnblogs.com/zhxilin/p/3799210.html 在前面的讨论中,我们介绍了如何在Unity3D for WP8中使用高于.Net 3.5的第三方库,传送门:http://www.cnblogs.com/zhxilin/p/3311240.html 在Unity3D和WP8的交互当中,如果要…

未来的程序员面临着怎样的职业变化
作为程序员,我们总是身处于如万花筒般变化无常的技术世界里。我们可能也是那群能够最早感知到科技变化所带来巨大影响的人。然而,面对这一波又一波向我们袭来的技术变革,我们是否也能从中窥见一丝规律,从而使自己更好地应对未来呢…