如何使用 Cancellationtoken使用 属性

君,已阅读到文档的结尾了呢~~
ASP.NET Core的配置(5):配置的同步[设计篇]
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
ASP.NET Core的配置(5):配置的同步[设计篇]
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口当前位置: →
→ C#综合揭秘——细说多线程下
C#综合揭秘——细说多线程下
& 作者及来源: 于为 - 博客园 &
&收藏到→_→:
摘要: C#综合揭秘——细说多线程(下)
"C#综合揭秘——细说多线程下"::
本文主要从线程的基础用法,clr线程池当中工作者线程与i/o线程的开发,并行操作plinq等多个方面介绍多线程的开发。其中委托的begininvoke 以及回调函数最为常用。而 i/o线程可能容易遭到大家的忽略,其实在开发多线程,更应该多留意i/o线程的操作。特别是在asp.net开发当中,可能更多人只会留意在客户端使用ajax或者在端使用updatepanel。其实合理使用i/o线程在通讯项目或文件下载时,能尽量降低iis的压力。并行编程是framework4.0中极力推广的异步操作方式,更值得更深入地学习。希望本篇文章能对各位的学习研究有所帮助,当中有所错漏的地方敬请点评。
一、线程的定义
二、线程的基础知识
三、以threadstart方式实现多线程
四、clr线程池的工作者线程
五、clr线程池的i/o线程
六、异步 sqlcommand
七、并行编程与plinq
八、计时器与锁
五、clr线程池的i/o线程
在前一节所介绍的线程都属于clr线程池的工作者线程,这一节开始为大家介绍一下clr线程池的i/o线程
i/o 线程是.net专为访问外部资源所设置的一种线程,因为访问外部资源常常要受到外界因素的影响,为了防止让主线程受影响而长期处于阻塞状态,.net为多个i/o操作都建立起了异步 ,例如:filestream、tcp/ip、webrequest、webservice等等,而且每个异步 的使用方式都非常类似,都是以beginxxx为开始,以endxxx结束,下面为大家一一解说。
5.1& 异步读写 filestream
需要在 filestream 异步调用 i/o线程,必须使用以下构造函数建立 filestream 对象,并把useasync设置为 true。
filestream stream = new filestream ( string path, filemode mode, fileaccess access, fileshare share, int buffersize,bool useasync ) ;
其中&path&是文件的相对路径或绝对路径;&mode&确定如何打开或创建文件;&access&确定访问文件的方式; share 确定文件如何进程共享; buffersize 是代表缓冲区大小,一般默认最小值为8,在启动异步读取或写入时,文件大小一般大于缓冲大小; userasync代表是否启动异步i/o线程。
注意:当使用 beginread 和 beginwrite
在执行大量读或写时效果更好,但对于少量的读/写,这些 速度可能比同步读取还要慢,因为进行线程间的切换需要大量时间。
5.1.1 异步写入
filestream中包含beginwrite、en ite
可以启动i/o线程进行异步写入。
public override iasyncresult beginwrite ( byte[] array, int offset, int numbytes, asynccallback usercallback, object stateobject )public override void en ite (iasyncresult asyncresult )
beginwrite 返回值为iasyncresult, 使用方式与委托的begininvoke 相似,最好就是使用回调函数,避免线程阻塞。在最后两个参数中,参数asynccallback用于绑定回调函数; 参数object用于传递外部数据。要注意一点:asynccallback所绑定的回调函数必须是带单个 iasyncresult 参数的无返回值 。在例子中,把filestream作为外部数据传递到回调函数当中,然后在回调函数中利用iasyncresult.asyncstate获取filestream对象,最后通过filestream.en ite(iasyncresult)结束写入。
class program 2
static void main(string[] args) 4
//把线程池的最大值设置为1000 6
threadpool.setmaxthreads(); 7
threadpoolmessage("start"); 8
//新立文件file.sour10
filestream stream = new filestream("file.sour", filemode.openorcreate, 11
fileaccess.rea ite,fileshare.rea ite,1024,true);12
byte[] bytes = new byte[16384];13
string message = "an operating-system threadid has no fixed relationship........";14
bytes = encoding.unicode.getbytes(message);15 16
//启动异步写入17
stream.beginwrite(bytes, 0, (int)bytes.length,new asynccallback(callback),stream);18
stream.flush();19
console.readkey();21
static void callback(iasyncresult result)24
//显示线程池现状26
thread.sleep(200);27
threadpoolmessage("asynccallback");28
//结束异步写入29
filestream stream = (filestream)result.30
stream.en ite(result);31
stream.close();32
//显示线程池现状35
static void threadpoolmessage(string data)36
threadpool.getavailablethreads(out a, out b);39
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}",41
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());42
console.writeline(message);43
由输出结果可以看到,在使用filestream.beginwrite 后,将自动启动clr线程池中i/o线程。
5.1.2 异步读取
filestream 中包含 beginread 与 endread 可以异步调用i/o线程进行读取。
public override iasyncresult beginread ( byte[] array,int offset,int numbytes, asynccallback usercallback,object stateobject)public override int endread(iasyncresult asyncresult)
其使用方式与beginwrite和en ite相似,asynccallback用于绑定回调函数; object用于传递外部数据。在回调函数只需要使用iasyncresut.asyncstate就可获取外部数据。en ite
会返回从流读取到的字节数量。
首先定义 filedata 类,里面包含filestream对象,byte[] 数组和长度。然后把filedata对象作为外部数据传到回调函数,在回调函数中,把iasyncresult.asyncstate强制转换为filedata,然后通过filestream.endread(iasyncresult)结束读取。最后比较一下长度,若读取到的长度此文来自: 马开东博客
转载请注明出处 网址:
与输入的数据长度不一至,则抛出异常。
class program 2
public class filedata 4
public byte[] 8
static void main(string[] args)11
//把线程池的最大值设置为100013
threadpool.setmaxthreads();14
threadpoolmessage("start");15
readfile();16
console.readkey();18
static void readfile()21
byte[] bytedata=new byte[];23
filestream stream = new filestream("file1.sour", filemode.openorcreate, 24
fileaccess.rea ite, fileshare.rea ite, 1024, true);25
//把filestream对象,byte[]对象,长度等有关数据绑定到filedata对象中,以附带属性方式送到回调函数27
filedata filedata = new filedata();28
filedata.stream =29
filedata.length = (int)stream.30
filedata.bytedata =31
//启动异步读取33
stream.beginread(bytedata, 0, filedata.length, new asynccallback(completed), filedata);34
static void completed(iasyncresult result)37
threadpoolmessage("completed");39
//把asyncresult.asyncstate转换为filedata对象,以filestream.endread完成异步读取41
filedata filedata = (filedata)result.42
int length=filedata.stream.endread(result);43
filedata.stream.close();44
//如果读取到的长度与输入长度不一致,则抛出异常46
if (length != filedata.length)47
throw new exception("stream is not complete!");48
string data=encoding.ascii.getstring(filedata.bytedata, 0, filedata.length);50
console.writeline(data.substring(2,22));51
//显示线程池现状54
static void threadpoolmessage(string data)55
threadpool.getavailablethreads(out a, out b);58
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}",60
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());61
console.writeline(message);
由输出结果可以看到,在使用filestream.beginread 后,将自动启动clr线程池中i/o线程。
注意:如果你看到的测试结果正好相反:工作者线程为999,i/o线程为1000,这是因为filestream的文件容量小于缓冲值1024所致的。此时文件将会一次性读取或写入,此文来自: 马开东博客
转载请注明出处 网址:
而将启动工作者线程而非i/o线程来处理回调函数。
5.2 异步操作tcp/ip套接字
在介绍 tcp/ip 套接字前先简单介绍一下 networkstream 类,它是用于网络访问的基础数据流。 networkstream 提供了好几个 控制套接字数据的发送与接收, 其中beginread、endread、beginwrite、en ite 能够实现异步操作,而且异步线程是来自于clr线程池的i/o线程。
public override int readbyte ()public override int read (byte[] buffer,int offset, int size)
public override void writebyte (byte value)public override void write (byte[] buffer,int offset, int size)
public override iasyncresult beginread (byte [] buffer, int offset, int size,& asynccallback callback, object state )public override int endread(iasyncresult result)
public override iasyncresult beginwrite (byte [] buffer, int offset, int size,& asynccallback callback, object state )public override void en ite(iasyncresult result)
若要创建 networkstream,必须提供已连接的 socket。而在.net中使用tcp/ip套接字不需要直接与socket打交道,因为.net把socket的大部分操作都放在system.net.tcplistener和system.net.sockets.tcpclient里面,这两个类大大地简化了socket的操作。一般套接字对象socket包含一个accept() ,此 能产生阻塞来等待客户端的请求,而在tcplistener类里也包含了一个相似的
public tcpclient accepttcpclient()用于等待客户端的请求。此 将会返回一个tcpclient 对象,通过 tcpclient 的 public networkstream getstream() 就能获取networkstream对象,控制套接字数据的发送与接收。
下面以一个例子说明异步调用tcp/ip套接字收发数据的 。
首先在端建立默认地址127.0.0.1用于收发信息,使用此地址与端口500新建tcplistener对象,调用tcplistener.start 侦听传入的连接请求,再使用一个死循环来监听信息。
在chatclient类包括有接收信息与发送信息两个功能:当接收到客户端请求时,它会利用 networkstream.beginread 读取客户端信息,并在回调函数receiveasynccallback中输出信息内容,若接收到的信息的大小小于1时,它将会抛出一个异常。当信息成功接收后,再使用 networkstream.beginwrite
回馈信息到客户端
class program 2
static void main(string[] args) 4
//设置clr线程池最大线程数 6
threadpool.setmaxthreads(); 7
//默认地址为127.0.0.1 9
ipaddress ipaddress = ipaddress.parse("127.0.0.1");10
tcplistener tcplistener = new tcplistener(ipaddress, 500);11
tcplistener.start();12
//以一个死循环来实现监听14
while (true)15
//调用一个chatclient对象来实现监听16
chatclient chatclient = new chatclient(tcplistener.accepttcpclient());
public class chatclient22
static byte[]25
static s26 27
public chatclient(tcpclient tcpclient1)28
tcpclient = tcpclient1;30
bytemessage = new byte[tcpclient.receivebuffersize];31
//显示客户端信息33
clientendpoint = tcpclient.client.remoteendpoint.tostring();34
console.writeline("client's endpoint is " + clientendpoint);35
//使用networkstream.beginread异步读取信息37
networkstream networkstream = tcpclient.getstream();38
networkstream.beginread(bytemessage, 0, tcpclient.receivebuffersize,39
new asynccallback(receiveasynccallback), null);40
public void receiveasynccallback(iasyncresult iasyncresult)43
//显示clr线程池状态45
thread.sleep(100);46
threadpoolmessage("\nmessage is receiving");47 48
//使用networkstream.endread结束异步读取49
networkstream networkstreamread = tcpclient.getstream();50
int length=networkstreamread.endread(iasyncresult);51 52
//如果接收到的数据长度少于1则抛出异常53
if (length & 1)54
tcpclient.getstream().close();56
throw new exception("disconnection!");57
//显示接收信息60
string message = encoding.utf8.getstring(bytemessage, 0, length);61
console.writeline("message:" + message);62 63
//使用networkstream.beginwrite异步发送信息64
byte[] sendmessage = encoding.utf8.getbytes("message is received!");65
networkstream networkstreamwrite=tcpclient.getstream();66
networkstreamwrite.beginwrite(sendmessage, 0, sendmessage.length, 67
new asynccallback(sendasynccallback), null);68
//把信息转换成二进制数据,然后发送到客户端71
public void sendasynccallback(iasyncresult iasyncresult)72
//显示clr线程池状态74
thread.sleep(100);75
threadpoolmessage("\nmessage is sending");76 77
//使用networkstream.en ite结束异步发送78
tcpclient.getstream().en ite(iasyncresult);79 80
//重新监听81
tcpclient.getstream().beginread(bytemessage, 0, tcpclient.receivebuffersize,82
new asynccallback(receiveasynccallback), null);83
//显示线程池现状86
static void threadpoolmessage(string data)87
threadpool.getavailablethreads(out a, out b);90
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}\n",92
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());93 94
console.writeline(message);95
而在客户端只是使用简单的开发方式,利用tcpclient连接到端,然后调用networkstream.write 发送信息,最后调用networkstream.read 读取回馈信息
static void main(string[] args) 2
//连接服务端 4
tcpclient tcpclient = new tcpclient("127.0.0.1", 500); 5
//发送信息 7
networkstream networkstream = tcpclient.getstream(); 8
byte[] sendmessage = encoding.utf8.getbytes("client request connection!"); 9
networkstream.write(sendmessage, 0, sendmessage.length);10
networkstream.flush();11 12
//接收信息13
byte[] receivemessage=new byte[1024];14
int count=networkstream.read(receivemessage, 0,1024);15
console.writeline(encoding.utf8.getstring(receivemessage));16
console.readkey();17
注意观察运行结果,端的异步操作线程都是来自此文来自: 马开东博客
转载请注明出处 网址:
于clr线程池的i/o线程
5.3 异步webrequest
system.net.webrequest&是 .net 为实现访问 &的 &请求/响应模型& 而开发的一个&abstract&基类,&它主要有三个子类:ftpwebrequest、httpwebrequest、filewebrequest。当使用webrequest.create(string uri)创建对象时,程序就可以根据请求协议判断实现类来进行操作。filewebrequest、ftpwebrequest、httpwebrequest 各有其作用:filewebrequest 使用 &file://路径& 的uri方式实现对本地资源和内部文件的请求/响应、ftpwebrequest 使用ftp文件传输协议实现文件请求/响应、httpwebrequest 用于处理http的页面请求/响应。由于使用 相类似,下面就以常用的httpwebrequest为例子介绍一下异步webrequest的使用 。
在使用asp.net开发网站的时候,往往会忽略了httpwebrequest的使用,因为开发都假设客户端是使用浏览器等去阅读页面的。但如果你对 开发方式有所了解,那对 httpwebrequest 就应该非常熟悉。它可以在路径参数、头文件、页面主体、cookie 等多处地方加入请求条件,然后对回复数据进行适当处理。httpwebrequest 包含有以下几个常用 用于处理请求/响应:
public override stream getrequeststream ()public override webresponse getresponse ()
public override iasyncresult begingetrequeststream ( asynccallback callback, object state )public override stream endgetrequeststream ( iasyncresult asyncresult )public override iasyncresult begingetresponse ( asynccallback callback, object state )public override webresponse endgetresponse ( iasyncresult asyncresult )
其中begingetrequeststream、endgetrequeststream 用于异步向httpwebrequest对象写入请求信息;& begingetresponse、endgetresponse 用于异步发送页面请求并获取返回信息。使用异步方式操作的&请求/响应&,避免主线程长期处于等待状态,而操作期间异步线程是来自clr线程池的i/o线程。
注意:请求与响应不能使用同步与异步混合开发模式,即当请求写入使用getrequeststream同步模式,即使响应使用begingetresponse异步 ,操作也与getrequeststream 在于同一线程内。
下面以简单的例子介绍一下异步请求的用法。
首先为person类加上可序列化特性,在端建立hanlder.ashx,通过request.inputstream 获取到请求数据并把数据转化为string对象,此实例中数据是以 &id:1& 的形式实现传送的。然后根据id查找对应的person对象,并把person对象写入response.outstream 中返还到客户端。
在客户端先把 httpwebrequird.method 设置为 "post",使用异步方式通过begingetrequi ream获取请求数据流,然后写入请求数据 &id:1&。再使用异步 begingetresponse 获取回复数据,最后把数据反序列化为person对象显示出来。
注意:httpwebrequire.method默认为get,在写入请求前必须把httpwebrequire.method设置为post,否则在使用begingetrequi ream 获取请求数据流的时候,就会发出 &无法发送具有此谓词类型的内容正文" 的异常。
1 namespace model 2 { 3
[serializable] 4
public class person 5
public int id 7
public string name12
public int age17
1 public class handler : ihttphandler { 2
public void processrequest(httpcontext context) 4
//把信息转换为string,找出输入条件id 6
byte[] bytes=new byte[1024]; 7
int length=context.request.inputstream.read(bytes,0,1024); 8
string condition = encoding.default.getstring(bytes); 9
int id = int.parse(condition.split(new string[] { ":" }, 10
stringsplitoptions.removeemptyentries)[1]);11
//根据id查找对应person对象13
var person = getpersonlist().where(x =& x.id == id).first();14
//所person格式化为二进制数据写入outputstream16
binaryformatter formatter = new binaryformatter();17
formatter.serialize(context.response.outputstream, person);18
//模拟源数据21
private ilist&person& getpersonlist()22
var personlist = new list&person&();24
var person1 = new person();26
person1.id = 1;27
person1.name = "leslie";28
person1.age = 30;29
personlist.add(person1);30
...........31
public bool isreusable35
class program 2
static void main(string[] args) 4
threadpool.setmaxthreads(); 6
request(); 7
console.readkey(); 8
static void request()11
threadpoolmessage("start"); 13
//使用webrequest.create 建立httpwebrequest对象14
httpwebrequest webrequest = (httpwebrequest)webrequest.create(15
"http://localhost:5700/handler.ashx");16
webrequest.method = "post";17
//对写入数据的requeststream对象进行异步请求19
iasyncresult result=webrequest.begingetrequeststream(20
new asynccallback(endgetrequeststream),webrequest);21
static void endgetrequeststream(iasyncresult result)24
threadpoolmessage("requeststream complete");26
//获取requeststream27
httpwebrequest webrequest = (httpwebrequest)result.28
stream stream=webrequest.endgetrequeststream(result);29 30
//写入请求条件31
byte[] condition = encoding.default.getbytes("id:1");32
stream.write(condition, 0, condition.length);33 34
//异步接收回传信息35
iasyncresult responseresult = webrequest.begingetresponse(36
new asynccallback(endgetresponse), webrequest);37
static void endgetresponse(iasyncresult result)40
//显出线程池现状42
threadpoolmessage("getresponse complete");43 44
//结束异步请求,获取结果45
httpwebrequest webrequest = (httpwebrequest)result.46
webresponse webresponse = webrequest.endgetresponse(result);47
//把输出结果转化为person对象49
stream stream = webresponse.getresponsestream();50
binaryformatter formatter = new binaryformatter();51
var person=(person)formatter.deserialize(stream);52
console.writeline(string.format("person
id:{0} name:{1} age:{2}",53
person.id, person.name, person.age));54
//显示线程池现状57
static void threadpoolmessage(string data)58
threadpool.getavailablethreads(out a, out b);61
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}\n",63
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());64 65
console.writeline(message);66
从运行结果可以看到,begingetrequi ream、begingetresponse 是使用clr线程池的i/o线程。
5.4 异步调用webservice
相比tcp/ip套接字,在使用webservice的时候,端需要更复杂的操作处理,使用时间往往会更长。为了避免客户端长期处于等待状态,在配置服务引用时选择 &生成异步操作&,可以自动建立异步调用的方式。
以.net 2.0以前,都是使用asmx来设计webservice,而近年来wcf可说是火热登场,下面就以wcf为例子简单介绍一下异步调用webservice的例子。
由于可以自动生成异步 ,使用起来非常简单,首先在端建立服务exampleservice,里面包含 method。客户端引用此服务时,选择 &生成异步操作&。然后使用 beginmethod 启动异步 , 在回调函数中调用endmethod结束异步调用。
[servicecontract] 2
public interface iexampleservice 3
[operationcontract] 5
string method(string name); 6
public class exampleservice : iexampleservice 9
public string method(string name)11
return "hello " +13
class program17
static void main(string[] args)19
servicehost host = new servicehost(typeof(exampleservice));21
host.open();22
console.readkey();23
host.close();24
&configuration&28
&system.servicemodel&29
&services&30
&service name="example.exampleservice"&31
&endpoint address="" binding="wshttpbinding" contract="example.iexampleservice"&32
&identity&33
&dns value="localhost" /&34
&/identity&35
&/endpoint&36
&endpoint address="mex" binding="mexhttpbinding" contract="imetadataexchange" /&37
&baseaddresses&39
&add baseaddress="http://localhost:7200/example/exampleservice/" /&40
&/baseaddresses&41
&/service&43
&/services&44
&/system.servicemodel&45
&/configuration&
class program 2
static void main(string[] args) 4
//设置最大线程数 6
threadpool.setmaxthreads(); 7
threadpoolmessage("start"); 8
//建立服务对象,异步调用服务 10
exampleservicereference.exampleserviceclient exampleservice = new11
exampleservicereference.exampleserviceclient();12
exampleservice.beginmethod("leslie",new asynccallback(asynccallbackmethod), 13
exampleservice);
console.readkey();15
static void asynccallbackmethod(iasyncresult result)18
thread.sleep(1000);20
threadpoolmessage("complete");21
exampleservicereference.exampleserviceclient example =22
(exampleservicereference.exampleserviceclient)result.23
string data=example.endmethod(result);24
console.writeline(data);25
//显示线程池现状28
static void threadpoolmessage(string data)29
threadpool.getavailablethreads(out a, out b);32
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}\n",34
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());35
console.writeline(message);37
&configuration&41
&system.servicemodel&42
&bindings&43
&wshttpbinding&44
&binding name="wshttpbinding_iexampleservice" closetimeout="00:01:00"45
opentimeout="00:01:00" receivetimeout="00:10:00" sendtimeout="00:01:00"46
bypassproxyonlocal="false" transactionflow="false" 47
hostnamecompar nmode="strongwildcard" maxbufferpoolsize="524288"48
maxreceivedmessagesize="65536" messageencoding="text" textencoding="utf-8"49
usedefaultwebproxy="true" allowcookies="false"&50
&readerquotas maxdepth="32" maxstringcontentlength="8192" maxarraylength="16384"51
maxbytesperread="4096" maxnametablecharcount="16384" /&52
&reliablesession ordered="true" inactivitytimeout="00:10:00" enabled="false" /&53
&security mode="message"&54
&transport clientcredentialtype="windows" proxycredentialtype="none"55
realm="" /&56
&message clientcredentialtype="windows" negotiateservicecredential="true"57
algorithmsuite="default" /&58
&/security&59
&/binding&60
&/wshttpbinding&61
&/bindings&62
&client&63
&endpoint address="http://localhost:7200/example/exampleservice/"64
binding="wshttpbinding" bindingconfiguration="wshttpbinding_iexampleservice"65
contract="exampleservicereference.iexampleservice" 66
name="wshttpbinding_iexampleservice"&67
&identity&68
&dns value="localhost" /&69
&/identity&70
&/endpoint&71
&/client&72
&/system.servicemodel&73
&/configuration&
注意观察运行结果,异步调用服务时,回调函数都是运行于clr线程池的i/o线程当中。
六、异步 sqlcommand
从ado.net 2.0开始,sqlcommand就新增了几个异步 执行sql命令。相对于同步执行方式,它使主线程不需要等待 的返回结果,在使用复杂性查询或批量插入时将有效提高主线程的效率。使用异步sqlcommand的时候,请注意把connectionstring 的&asynchronous processing 设置为 true 。
注意:sqlcommand异步操作的特别之处在于线程并不依赖于clr线程池,而是由windows内部提供,这比使用异步委托更有效率。但如果需要使用回调函数的时候,回调函数的线程依然是来自于clr线程池的工作者线程。
sqlcommand有以下几个 支持异步操作:
public iasyncresult beginexecutenonquery (......)public int endexecutenonquery(iasyncresult)
public iasyncresult beginexecutereader(......)public sqldatareader endexecutereader(iasyncresult)
public iasyncresult beginexecutexmlreader (......)public xmlreader endexecutexmlreader(iasyncresult)
由于使用方式相似,此处就以 beginexecutenonquery 为例子,介绍一下异步sqlcommand的使用。首先建立connectionstring,注意把asynchronous processing设置为true来启动异步命令,然后把mandtext设置为 waitfor delay "0:0:3" 来虚拟 操作。再通过beginexecutenonquery启动异步操作,利用轮询方式监测操作情况。最后在操作完成后使用endexecutenonquery完成异步操作。
class program 2
//把asynchronous processing设置为true 4
static string connectionstring = "data source=leslie-initial catalog=&+ 5
"integrated security=asynchronous processing=true"; 6
static void main(string[] args) 8
//把clr线程池最大线程数设置为100010
threadpool.setmaxthreads();11
threadpoolmessage("start");12 13
//使用waitfor delay命令来虚拟操作14
sqlconnection connection = new sqlconnection(connectionstring);15
sqlcommand command = new sqlcommand("waitfor delay '0:0:3';", connection);16
connection.open();17 18
//启动异步sqlcommand操作,利用轮询方式监测操作19
iasyncresult result = command.beginexecutenonquery();20
threadpoolmessage("beginread");21
while (!result.asyncwaithandle.waitone(500))22
console.writeline("main thread do work........");23 24
//结束异步sqlcommand25
int count= command.endexecutenonquery(result);26
threadpoolmessage("\ncompleted");27
console.readkey();28
//显示线程池现状31
static void threadpoolmessage(string data)32
threadpool.getavailablethreads(out a, out b);35
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}\n",37
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());38
console.writeline(message);39
注意运行结果,sqlcommand的异步执行线程并不属于clr线程池。
如果觉得使用轮询方式过于麻烦,可以使用回调函数,但要注意当调用回调函数时,线程是来自于clr线程池的工作者线程。
class program 2
//把asynchronous processing设置为true 4
static string connectionstring = "data source=leslie-initial catalog=&+ 5
&integrated security=asynchronous processing=true"; 6
static void main(string[] args) 7
//把clr线程池最大线程数设置为1000 9
threadpool.setmaxthreads();10
threadpoolmessage("start");11 12
//使用waitfor delay命令来虚拟操作13
sqlconnection connection = new sqlconnection(connectionstring);14
sqlcommand command = new sqlcommand("waitfor delay '0:0:3';", connection);15
connection.open();16
//启动异步sqlcommand操作,并把sqlcommand对象传递到回调函数18
iasyncresult result = command.beginexecutenonquery(19
new asynccallback(asynccallbackmethod),command);20
console.readkey();21
static void asynccallbackmethod(iasyncresult result)24
thread.sleep(200);26
threadpoolmessage("asynccallback");27
sqlcommand command = (sqlcommand)result.28
int count=command.endexecutenonquery(result);29
command.connection.close();30
//显示线程池现状33
static void threadpoolmessage(string data)34
threadpool.getavailablethreads(out a, out b);37
string message = string.format("{0}\n
currentthreadid is {1}\n
"workerthreads is:{2}
completionportthreads is :{3}\n",39
data, thread.currentthread.managedthreadid, a.tostring(), b.tostring());40 41
console.writeline(message);42
运行结果:
七、并行编程与plinq
要使用多线程开发,必须非常熟悉thread的使用,而且在开发 中可能会面对很多未知的问题。为了简化开发,.net 4.0 特别提供一个并行编程库system.threading.tasks,它可以简化并行开发,你无需直接跟线程此文来自: 马开东博客
转载请注明出处 网址:
或线程池打交道,就可以简单建立多线程程序。此外,.net还提供了新的一组扩展 plinq,它具有自动分析查询功能,如果并行查询能提高效率,则同时运行,如果查询未能从并行查询中受益,则按原顺序查询。下面将详细介绍并行操作的方式。
7.1 泛型委托
使用并行编程可以同时操作多个委托,在介绍并行编程前先简单介绍一下两个泛型委托system.func&&与system.action&&。
func&&是一个能接受多个参数和一个返回值的泛型委托,它能接受0个到16个输入参数, 其中 t1,t2,t3,t4......t16 代表自定的输入类型,tresult为自定义的返回值。public delegate tresult func&tresult&()public delegate tresult func&t1,tresult&(t1 arg1)public delegate tresult func&t1,t2, tresult&(t1 arg1,t2 arg2)public delegate tresult func&t1,t2, t3, tresult&(t1 arg1,t2 arg2,t3 arg3)public delegate tresult func&t1,t2, t3, ,t4, tresult&(t1 arg1,t2 arg2,t3 arg3,t4 arg4)..............public delegate tresult func&t1,t2, t3, ,t4, ...... ,t16,tresult&(t1 arg1,t2 arg2,t3 arg3,t4 arg4,...... ,t16 arg16)
action&&与func&&十分相似,不同在于action&&的返回值为void,action能接受0~16个参数public delegate void action&t1&()public delegate void action&t1,t2&(t1 arg1,t2 arg2)public delegate void action&t1,t2, t3&(t1 arg1,t2 arg2, t3 arg3).............public delegate void action&t1,t2, t3, ,t4, ...... ,t16&(t1 arg1,t2 arg2,t3 arg3,t4 arg4,...... ,t16 arg16)
7.2 任务并行库(tpl)
system.threading.tasks中的类被统称为任务并行库(task parallel library,tpl),tpl使用clr线程池把工作分配到cpu,并能自动处理工作分区、线程调度、取消支持、状态管理以及其他低级别的细节操作,极大地简化了多线程的开发。
注意:tpl比thread更具智能性,当它判断任务集并没有从并行运行中受益,就会选择按顺序运行。但并非所有的项目都适合使用并行开发,创建过多并行任务可能会损害程序的性能,降低运行效率。
tpl包括常用的数据并行与任务并行两种执行方式:
7.2.1 数据并行
数据并行的核心类就是system.threading.tasks.parallel,它包含两个静态
parallel.for 与 parallel.foreach, 使用方式与for、foreach相仿。通过这两个 可以并行处理system.func&&、system.action&&委托。
以下一个例子就是利用 public static parallelloopresult for( int from, int max, action&int&)
对list&person&进行并行查询。假设使用单线程方式查询3个person对象,需要用时大约6秒,在使用并行方式,只需使用2秒就能完成查询,而且能够避开thread的繁琐处理。
class program 2
static void main(string[] args) 4
//设置最大线程数 6
threadpool.setmaxthreads(); 7
//并行查询 8
parallel.for(0, 3,n =& 9
thread.sleep(2000);
//模拟查询11
threadpoolmessage(getpersonlist()[n]);12
console.readkey();14
//模拟源数据17
static ilist&person& getpersonlist()18
var personlist = new list&person&();20 21
var person1 = new person();22
person1.id = 1;23
person1.name = "leslie";24
person1.age = 30;25
personlist.add(person1);26
...........27
//显示线程池现状31
static void threadpoolmessage(person person)32
threadpool.getavailablethreads(out a, out b);35
string message = string.format("person
id:{0} name:{1} age:{2}\n" +36
currentthreadid is {3}\n
workerthreads is:{4}" +37
completionportthreads is :{5}\n",38
person.id, person.name, person.age,39
thread.currentthread.managedthreadid, a.tostring(), b.tostring());40 41
console.writeline(message);42
观察运行结果,对象并非按照原排列顺序进行查询,而是使用并行方式查询。
若想停止操作,可以利用parallelloopstate参数,下面以foreach作为例子。public static parallelloopresult foreach&tsource&( ienumerable&tsource& source, action&tsource, parallelloopstate& action)其中source为数据集,在action&tsource,parallelloopstate&委托的parallelloopstate参数当中包含有break()和 stop()两个 都可以使迭代停止。break的使用跟传统for里面的使用方式相似,但因为处于并行处理当中,使用break并不能保证所有运行能立即停止,在当前迭代之前的迭代会继续执行。若想立即停止操作,可以使用stop ,它能保证立即终止所有的操作,无论它们是处于当前迭代的之前还是之后。
class program 2
static void main(string[] args) 4
//设置最大线程数 6
threadpool.setmaxthreads(); 7
//并行查询 9
parallel.foreach(getpersonlist(), (person, state) =&10
if (person.id == 2)12
state.stop();13
threadpoolmessage(person);14
console.readkey();16
//模拟源数据19
static ilist&person& getpersonlist()20
var personlist = new list&person&();22
var person1 = new person();24
person1.id = 1;25
person1.name = "leslie";26
person1.age = 30;27
personlist.add(person1);28
..........29
//显示线程池现状33
static void threadpoolmessage(person person)34
threadpool.getavailablethreads(out a, out b);37
string message = string.format("person
id:{0} name:{1} age:{2}\n" +38
currentthreadid is {3}\n
workerthreads is:{4}" +39
completionportthreads is :{5}\n",40
person.id, person.name, person.age,41
thread.currentthread.managedthreadid, a.tostring(), b.tostring());42
console.writeline(message);44
观察运行结果,当person的id等于2时,运行将会停止。
当要在多个线程中调用本地变量,可以使用以下 :public static parallelloopresult foreach&tsource, tlocal&(ienumerable&of tsource&, func&of tlocal&, func&of tsource,parallelloopstate,tlocal,tlocal&, action&of tlocal&)其中第一个参数为数据集;第二个参数是一个func委托,用于在每个线程执行前进行初始化;第 三个参数是委托func&of t1,t2,t3,tresult&,它能对数据集的每个成员进行迭代,当中t1是数据集的成员,t2是一个parallelloopstate对 象,它可以控制迭代的状态,t3是线程中的本地变量;第四个参数是一个action委托,用于对每个线程的最终状态进行最终操作。
在以下例子中,使用foreach计算多个order的总体价格。在foreach 中,首先把参数初始化为0f,然后用把同一个order的多个orderitem价格进行累加,计算出order的价格,最后把多个order的价格进行累加,计算出多个order的总体价格。
public class order 2
public class orderitem 8
class program17
static void main(string[] args)19
//设置最大线程数21
threadpool.setmaxthreads();22
float totalprice = 0f;23
//并行查询24
var parallelresult = parallel.foreach(getorderlist(),25
//把参数初始值设为026
(order, state, orderprice) =&27
//计算单个order的价格29
orderprice = getorderitem().where(item =& item.orderid == order.id)30
.sum(item =& item.price * item.count);31
order.price =32
threadpoolmessage(order);33
(finallyprice) =&37
totalprice +=//计算多个order的总体价格39
while (!parallelresult.iscompleted)43
console.writeline("doing work!");44 45
console.writeline("total price is:" + totalprice);46
console.readkey();47
//虚拟数据49
static ilist&order& getorderlist()50
ilist&order& orderlist = new list&order&();52
order order1 = new order();53
order1.id = 1;54
orderlist.add(order1);55
............56
//虚拟数据59
static ilist&orderitem& getorderitem()60
ilist&orderitem& itemlist = new list&orderitem&();62 63
orderitem orderitem1 = new orderitem();64
orderitem1.id = 1;65
orderitem1.goods = "iphone 4s";66
orderitem1.price = 6700;67
orderitem1.count = 2;68
orderitem1.orderid = 1;69
itemlist.add(orderitem1);70
...........71
//显示线程池现状75
static void threadpoolmessage(order order)76
threadpool.getavailablethreads(out a, out b);79
string message = string.format("orderid:{0}
orderprice:{1}\n" +80
currentthreadid is {2}\n
workerthreads is:{3}" +81
completionportthreads is:{4}\n",82
order.id, order.price,83
thread.currentthread.managedthreadid, a.tostring(), b.tostring());84 85
console.writeline(message);86
&7.2.2 任务并行
在tpl当中还可以使用parallel.invoke 触发多个异步任务,其中 actions 中可以包含多个 或者委托,paralleloptions用于配置parallel类的操作。public static void invoke(action[] actions )public static void invoke(paralleloptions paralleloptions, action[] actions )下面例子中利用了parallet.invoke并行查询多个person,actions当中可以绑定 、lambda表达式或者委托,注意绑定 时必须是返回值为void的无参数 。
class program 2
static void main(string[] args) 4
//设置最大线程数 6
threadpool.setmaxthreads(); 7
//任务并行 9
parallel.invoke(option,10
personmessage, 11
()=&threadpoolmessage(getpersonlist()[1]),
delegate(){13
threadpoolmessage(getpersonlist()[2]);14
console.readkey();16
static void personmessage()19
threadpoolmessage(getpersonlist()[0]);21
//显示线程池现状24
static void threadpoolmessage(person person)25
threadpool.getavailablethreads(out a, out b);28
string message = string.format("person
id:{0} name:{1} age:{2}\n" +29
currentthreadid is {3}\n
workerthreads is:{4}" +30
completionportthreads is :{5}\n",31
person.id, person.name, person.age,32
thread.currentthread.managedthreadid, a.tostring(), b.tostring());33 34
console.writeline(message);35
//模拟源数据38
static ilist&person& getpersonlist()39
var personlist = new list&person&();41 42
var person1 = new person();43
person1.id = 1;44
person1.name = "leslie";45
person1.age = 30;46
personlist.add(person1);47
..........48
7.3 task简介
以thread创建的线程被默认为前台线程,当然你可以把线程isbackground属性设置为true,但tpl为此提供了一个更简单的类task。task存在于system.threading.tasks当中,它可以作为异步委托的简单替代品。通过task的factory属性将返回taskfactory类,以taskfactory.startnew(action) 可以创建一个新线程,所创建的线程默认为后台线程。
class program 2
static void main(string[] args) 4
threadpool.setmaxthreads(); 6
task.factory.startnew(() =& threadpoolmessage()); 7
console.readkey(); 8
//显示线程池现状11
static void threadpoolmessage()12
threadpool.getavailablethreads(out a, out b);15
string message = string.format("currentthreadid is:{0}\n" +16
"currentthread isbackground:{1}\n" +17
"workerthreads is:{2}\ncompletionportthreads is:{3}\n",18
thread.currentthread.managedthreadid,19
thread.currentthread.isbackground.tostring(),20
a.tostring(), b.tostring());21
console.writeline(message);22
若要取消处理,可以利用cancellationtakensource对象,在taskfactory中包含有 public task startnew( action action, cancellationtoken cancellationtoken )在 中加入cancellationtakensource对象的cancellationtoken属性,可以控制任务的运行,调用cancellationtakensource.cancel时任务就会自动停止。下面以图片下载为例子介绍一下taskfactory的使用。
1 &html xmlns="http://www.w3.org/1999/xhtml"& 2 &head runat="server"& 3
&title&&/title& 4
&script type="text/c#" runat="server"& 5
private static list&string& url=new list&string&(); 6
protected void page_load(object sender, eventargs e) 8
if (!page.ispostback)10
url.clear();12
application["url"] =13
protected void checkbox_checkedchanged(object sender, eventargs e)17
checkbox checkbox = (checkbox)19
if (checkbox.checked)20
url.add(checkbox.text);21
url.remove(checkbox.text);23
application["url"]=24
}25 &/script&26 &/head&27 &body&28
&form id="form1" runat="server" &29
&div align="left"&30
&div align="center" style="float:"&31
&asp:image id="image1" runat="server" imageurl="~/images/a.jpg" /&&br /&32
&asp:checkbox id="checkbox1" runat="server" autopostback="true" 33
oncheckedchanged="checkbox_checkedchanged" text="a.jpg" /&34
&div align="center" style="float: left"&36
&asp:image id="image2" runat="server" imageurl="~/images/b.jpg" /&&br /&37
&asp:checkbox id="checkbox2" runat="server" autopostback="true" 38
oncheckedchanged="checkbox_checkedchanged" text="b.jpg" /&39
&div align="center" style="float: left"&41
&asp:image id="image3" runat="server" imageurl="~/images/c.jpg" /&&br /&42
&asp:checkbox id="checkbox3" runat="server" autopostback="true" 43
oncheckedchanged="checkbox_checkedchanged" text="c.jpg" /&44
&div align="center" style="float: left"&46
&asp:image id="image4" runat="server" imageurl="~/images/d.jpg" /&&br /&47
&asp:checkbox id="checkbox4" runat="server" autopostback="true" 48
oncheckedchanged="checkbox_checkedchanged" text="d.jpg" /&49
&div align="center" style="float: left"&51
&asp:image id="image5" runat="server" imageurl="~/images/e.jpg" /&&br /&52
&asp:checkbox id="checkbox5" runat="server" autopostback="true" 53
oncheckedchanged="checkbox_checkedchanged" text="e.jpg" /&54
&/form&57 &/body&58 &/html&
首先在页面中显示多个*.jpg图片,每个图片都有对应的checkbox检测其选择情况。所选择图片的路径会记录在application["url"]当中传递到handler.ashx当中。
注意:application是一个全局变量,此处只是为了显示task的使用方式,在asp.net开发应该慎用application。
handler.ashx 处理图片的下载,它从 application["url"] 当中获取所选择图片的路径,并把图片转化成byte[]二进制数据。再把图片的数量,每副图片的二进制数据的长度记录在outputstream的头部。最后把图片的二进制数据记入 outputstream 一并输出。
1 public class handler : ihttphandler
public void processrequest(httpcontext context) 4
//获取图片名,把图片数量写outputstream 6
list&string& urllist = (list&string&)context.application["url"]; 7
context.response.outputstream.write(bitconverter.getbytes(urllist.count), 0, 4); 8
//把图片转换成二进制数据10
list&string& imagelist = getimages(urllist);11
//把每副图片长度写入outputstream13
foreach (string image in imagelist)14
byte[] imagebyte=convert.frombase64string(image);16
context.response.outputstream.write(bitconverter.getbytes(imagebyte.length),0,4);17
//把图片写入outputstream20
foreach (string image in imagelist)21
byte[] imagebyte = convert.frombase64string(image);23
context.response.outputstream.write(imagebyte,0,imagebyte.length);24
//获取多个图片的二进制数据28
private list&string& getimages(list&string& urllist)29
list&string& imagelist = new list&string&();31
foreach (string url in urllist)32
imagelist.add(getimage(url));33
//获取单副图片的二进制数据37
private string getimage(string url)38
string path = "e:/my projects/example/website/images/"+40
filestream stream = new filestream(path, filemode.open, fileaccess.read);41
byte[] imgbytes = new byte[10240];42
int imglength = stream.read(imgbytes, 0, 10240);
return convert.tobase64string(imgbytes,0,imglength);44
public bool isreusable47
建立一个winform窗口,里面加入一个webbrowser连接到端的default.aspx页面。当按下download按键时,就会利用taskfactory.startnew的 建立异步线程,使用webrequest 向handler.ashx发送请求。接收到回传流时,就会根据头文件的内容判断图片的数量与每副图片的长度,把二进制数据转化为*.jpg文件保存。
利用taskfactory.startnew(action,cancellationtoken) 方式异步调用getimages 进行图片下载。&当用户按下cancel按钮时,异步任务就会停止。值得注意的是,在图片下载时调用了cancellationtoken.throwifcancellationrequested ,目的在检查并行任务的运行情况,在并行任务被停止时释放出operationcanceledexception异常,确保用户按下cancel按钮时,停止所有并行任务。
public partial class form1 : form 2
private cancellationtokensource tokensource = new cancellationtokensource(); 4
public form1() 6
initializecomponent(); 8
threadpool.setmaxthreads(); 9
private void downloadtoolstripmenuitem_click(object sender, eventargs e)12
task.factory.startnew(getimages,tokensource.token);14
private void canceltoolstripmenuitem_click(object sender, eventargs e)17
tokensource.cancel();19
private void getimages()22
//发送请求,获取输出流24
webrequest webrequest = httpwebrequest.create("http://localhost:5800/handler.ashx"); 25
stream responsestream=webrequest.getresponse().getresponsestream();26 27
byte[] responsebyte = new byte[81960];28
iasyncresult result=responsestream.beginread(responsebyte,0,81960,null,null);29
int responselength = responsestream.endread(result);30 31
//获取图片数量32
int imagecount = bitconverter.toint32(responsebyte, 0);33
//获取每副图片的长度35
int[] lengths = new int[imagecount];36
for (int n = 0; n & n++)37
int length = bitconverter.toint32(responsebyte, (n + 1) * 4);39
lengths[n] =40
//保存图片44
for (int n = 0; n & n++)45
string path = string.format("e:/my projects/example/test/images/pic{0}.jpg", n);47
filestream file = new filestream(path, filemode.create, fileaccess.rea ite);48 49
//计算字节偏移量50
int offset = (imagecount + 1) * 4;51
for (int a = 0; a & a++)52
offset += lengths[a];53 54
file.write(responsebyte, offset, lengths[n]);55
file.flush();56 57
//模拟操作58
thread.sleep(1000);59 60
//检测cancellationtoken变化61
tokensource.token.throwifcancellationrequested();62
catch (operationcanceledexception ex)65
messagebox.show("download cancel!");67
7.4 并行查询(plinq)
并行 linq (plinq) 是 linq 模式的并行实现,主要区别在于 plinq 尝试充分利用中的所有处理器。&它利用所有处理器的 ,把数据源分成片段,然后在多个处理器上对单独工作线程上的每个片段并行执行查询,&在许多情况下,并行执行意味着查询运行速度显著提高。但这并不说明所有plinq都会使用并行方式,当测试要并行查询会对性能造成损害时,那将地使用同步执行。在system.linq.parallelenumerable类中,包含了并行查询的大部分 。&
asparallel
plinq 的入口点。&指定如果可能,应并行化查询的其余部分。
assequential(of&tsource)
指定查询的其余部分应像非并行 linq 查询一样按顺序运行。
指定 plinq 应保留查询的其余部分的源序列排序,直到例如通过使用 orderby(在 visual basic 中为 order by)子句更改排序为止。
asunordered(of&tsource)
指定查询的其余部分的 plinq 不需要保留源序列的排序。
withcancellation(oftsource)
指定 plinq 应定期监视请求取消时提供的取消标记和取消执行的状态。
withdegreeofparallelism(oftsource)
指定 plinq 应当用来并行化查询的处理器的最大数目。
withmergeoptions(oftsource)
提供有关 plinq 应当如何(如果可能)将并行结果合并回到使用线程上的一个序列的提示。
withexecutionmode(oftsource)
指定 plinq 应当如何并行化查询(即使默认行为是按顺序运行查询)。
forall(of&tsource)
多线程枚举 ,与循环访问查询结果不同,它允许在不首先合并回到使用者线程的情况下并行处理结果。
aggregate&重载
对于 plinq 唯一的重载,它启用对线程本地分区的中间聚合以及一个用于合并所有分区结果的最终聚合函数。
7.4.1 asparallel
通常想要实现并行查询,只需向数据源添加&asparallel&查询操作即可。
class program 2
static void main(string[] args) 4
var personlist=getpersonlist().asparallel()
.where(x=&x.age&30); 7
console.readkey(); 8
//模拟源数据11
static ilist&person& getpersonlist()12
var personlist = new list&person&();14 15
var person1 = new person();16
person1.id = 1;17
person1.name = "leslie";18
person1.age = 30;19
personlist.add(person1);20
...........21
7.4.2 asordered
若要使查询结果必须保留源序列排序方式,可以使用asordered 。&asordered依然使用并行方式,只是在查询 加入额外信息,在并行结束后把查询结果再次进行排列。
class program 2
static void main(string[] args) 4
var personlist=getpersonlist().asparallel().asordered() 6
.where(x=&x.age&30); 7
console.readkey(); 8
static ilist&person& getpersonlist()11
{......}12
7.4.3 withdegreeofparallelism
默认情况下,plinq 使用主机上的所有处理器,这些处理器的数量最多可达 64 个。通过使用 withdegreeofparallelism(of tsource)
,可以指示 plinq 使用不多于指定数量的处理器。
class program 2
static void main(string[] args) 4
var personlist=getpersonlist().asparallel().withdegreeofparallelism(2) 6
.where(x=&x.age&30); 7
console.readkey(); 8
static ilist&person& getpersonlist()11
{.........}12
7.4.4 forall
如果要对并行查询结果进行操作,一般会在for或foreach中执行,执行枚举操作时会使用同步方式。有见及此,plinq中包含了forall ,它可以使用并行方式对数据集进行操作。
class program 2
static void main(string[] args) 4
threadpool.setmaxthreads(); 6
getpersonlist().asparallel().forall(person =&{ 7
threadpoolmessage(person); 8
console.readkey();10
static ilist&person& getpersonlist()13
{.......}14 15
//显示线程池现状16
static void threadpoolmessage(person person)17
threadpool.getavailablethreads(out a, out b);20
string message = string.format("person
id:{0} name:{1} age:{2}\n" +21
currentthreadid is {3}\n
workerthreads is:{4}" +22
completionportthreads is :{5}\n",23
person.id, person.name, person.age,24
thread.currentthread.managedthreadid, a.tostring(), b.tostring());25
console.writeline(message);26
7.4.5 withcancellation
如果需要停止查询,可以使用 withcancellation(of tsource) 运算符并提供 cancellationtoken 实例作为参数。&与第三节task的例子相似,如果标记上的 iscancellationrequested 属性设置为 true,则 plinq 将会注意到它,并停止所有线程上的处理,然后引发 operationcanceledexception。这可以保证并行查询能够立即停止。
class program 2
static cancellationtokensource tokensource = new cancellationtokensource(); 4
static void main(string[] args) 6
task.factory.startnew(cancel); 8
getpersonlist().asparallel().withcancellation(tokensource.token)11
.forall(person =&12
threadpoolmessage(person);14
catch (operationcanceledexception ex)17
console.readkey();19
//在10~50毫秒内发出停止信号22
static void cancel()23
random random = new random();25
thread.sleep(random.next(10,50));26
tokensource.cancel();27
static ilist&person& getpersonlist()30
{......}31 32
//显示线程池现状33
static void threadpoolmessage(person person)34
threadpool.getavailablethreads(out a, out b);37
string message = string.format("person
id:{0} name:{1} age:{2}\n" +38
currentthreadid is {3}\n
workerthreads is:{4}" +39
completionportthreads is :{5}\n",40
person.id, person.name, person.age,41
thread.currentthread.managedthreadid, a.tostring(), b.tostring());42
console.writeline(message);43
八、定时器与锁
若要长期定时进行一些工作,比如像邮箱更新,实时收听信息等等,可以利用定时器timer进行操作。在system.threading中存在timer类与对应的timercallback委托,它可以在后台线程中执行一些长期的定时操作,使主线程不受干扰。timer类中最常用的构造函数为 public timer( timercallback , object , int , int )timercallback委托可以绑定执行 ,执行 必须返回void,它可以是无参数 ,也可以带一个object参数的 。第二个参数是为 timercallback 委托输入的参数对象。第三个参数是开始执行前等待的时间。第四个参数是每次执行之间的等待时间。
class program 2
static void main(string[] args) 4
threadpool.setmaxthreads(); 6
timercallback callback = new timercallback(threadpoolmessage); 8
timer t = new timer(callback,"hello jack! ", 0, 1000); 9
console.readkey();10
//显示线程池现状13
static void threadpoolmessage(object data)14
threadpool.getavailablethreads(out a, out b);17
string message = string.format("{0}\n
currentthreadid is:{1}\n" +18
currentthread isbackground:{2}\n" +19
workerthreads is:{3}\n
completionportthreads is:{4}\n",20
data + "time now is " + datetime.now.tolongtimestring(),21
thread.currentthread.managedthreadid,22
thread.currentthread.isbackground.tostring(),23
a.tostring(), b.tostring());24
console.writeline(message);25
注意观察运行结果,每次调用timer绑定的 时不一定是使用同一线程,但线程都会是来自工作者线程的后台线程。
在使用多线程开发时,存在一定的共用数据,为了避免多线程同时操作同一数据,.net提供了lock、monitor、interlocked等多个锁定数据的方式。
8.2.1 lock
lock的使用比较简单,如果需要锁定某个对象时,可以直接使用lock(this)的方式。
1 private void method()2 {3
lock(this)4
//在此进行的操作能保证在同一时间内只有一个线程对此对象操作6
如果操作只锁定某段代码,可以事先建立一个object对象,并对此对象进行操作锁定,这也是.net提倡的锁定用法。
1 class control 2 { 3
private object obj=new object(); 4
public void method() 6
lock(obj) 8
{.......} 9
8.2.2 montior
montior存在于system.thread内,相比lock,montior使用更灵活。它存在 enter, exit 两个 ,它可以对对象进行锁定与解锁,比lock使用更灵活。
1 class control 2 { 3
private object obj=new object(); 4
public void method() 6
monitor.enter(obj); 8
{......}10
catch(excetion ex)11
{......}12
monitor.exit(obj);15
使用try的方式,能确保程序不会因死锁而释放出异常!而且在finally中释放obj对象能够确保无论是否出现死锁状态,都会释放obj对象。而且monitor中还存在wait 可以让线程等待一段时间,然后在完成时使用pulse、pulseall等 通知等待线程。
8.2.3 interlocked
interlocked存在于system.thread内,它的操作比monitor使用更简单。它存在compareexchange、decrement、exchange、increment等常用 让参数在安全的情况进行数据交换。
increment、decrement 可以使参数安全地加1或减1并返回递增后的新值。
搜索此文相关文章: 揭秘——细说多线程下此文来自: 马开东博客
网址: 站长QQ
C#综合揭秘——细说多线程下_博客园相关文章
博客园_总排行榜
博客园_最新
博客园_月排行榜
博客园_周排行榜
博客园_日排行榜

我要回帖

更多关于 token如何使用 的文章

 

随机推荐