首页 新闻 会员 周边

C# 里异步方法的疑问

1
悬赏园豆:100 [待解决问题]

C#里很多支持异步方法调用的函数;比如TCPClient的BeginReceive,BeginWrite等;

但不明白的是BeginReceive调用后,系统是如何调配线程来执行这个函数的;

一般接受数据的写法是:

clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,SocketFlags.None,newAsyncCallback(RecieveComplete), clientSocket);
而这个回调里会继续调用BeginReceive,以持续不断地接收从服务器发来的数据。

我想问的是这样每次都异步投递,线程的切换不是很频繁,会不会开销大?

对C#线程机制不熟悉,请指教。谢谢。
 
hailong的主页 hailong | 初学一级 | 园豆:70
提问于:2014-07-12 14:38
< >
分享
所有回答(2)
0

BeginReceive 会一直等,只到接收命令;线程很多可以用线程池管理,BeginReceive 等待的时占用资源很低的,我也刚刚学习,压力测试也是几千个

maanshancss | 园豆:303 (菜鸟二级) | 2014-07-13 23:40
0

BeginXXX 会将 SOCKET 绑定到线程池,即调用 BindIoCompletionCallback,在新的 Threadpool API 之前,回调方法会由 IO 线程池中的线程来执行,这就是常说的 IOCP 特性。正因为如此,我们有一个针对 BeginXXX 之类的方法调用中的回调方法的优化建议,尽可能短小,不要包含阻塞式 IO 调用,尽量不要使用内核态同步数据结构。

 

线程切换的问题是针对整个进程而言的,准确的讲是上下文切换,对单个操作没有意义,因为虽然有可能异步投递的线程和回调线程不是同一个线程,但是只要回调线程没有被挂起(比如此线程一直在执行一个 100 的阶乘的运算,刚执行完毕就被分配来执行此回调函数),那么就不会产生上下文切换。

Launcher | 园豆:45045 (高人七级) | 2014-07-14 08:54

BeginXXX里传入的callback会在IOCP回调里被调用,即便里面有阻塞也是在IO线程吧,这会有影响?请指教。

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 12:35

@hailong: 我先问你个问题,有使用完成端口编写过线程池吗?

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-07-14 13:11

@Launcher: 没有,都是用封装好的库,如C++下boost::asio,libevent等

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 13:12

@hailong: 那我只能简单解释下,剩下的你需要补下完成端口和线程池的知识。当 IO 线程被阻塞后,它就会被挂起,如果此时完成端口上仍然有就绪的请求,并且没有达到完成端口的并发上线,那么线程池会创建一个新的 IO 线程来满足请求。设想下,当此类情况经常发生时,IO 线程池达到上限,此时所有 IO 线程都被挂起,就绪的请求就得不到满足,但是此时 CPU 却是无事可干,因为所有 IO 线程都在等待 IO 或者内核的唤醒。在新的 Threadpool API 中,系统不区分 IO 线程和工作线程,那么遇到这种情况就更麻烦,因为你甚至都没有可用的工作线程去处理计算任务。

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-07-14 13:20

@Launcher: 嗯,线程池我是理解,IO线程通常也不会阻塞,如果是beginReceived收到数据会push的队列,worker线程会不断检测处理。

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 13:24

@hailong: 通常会不会阻塞,不是系统提供的能力,而是你写的代码产生的影响,举个例子:

clientSocket.BeginReceive(xxxx,o=> {

      clientSocket.Receive();  // 这句代码就会阻塞 IO 线程,就会产生上下文切换

      // 或者像下面这样:

     WriteFile(); // no overlapped.

 

    // 或者这样:

    SqlConnecton.Open();

    SqlCommand.Execute();

} );

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-07-14 13:35

@Launcher: 我已经检测代码了,里面没有任何阻塞调用。像你举的例子都是阻塞IO调用,肯定会有问题。

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 13:37

@hailong: 

clientSocket.BeginReceive(xxxx,o=> {

      lock(obj){//dosomething;} // 视 dosomething 执行时间长短而定;

   

   // 或者

    WaitForXXXXObject();

});

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-07-14 13:39

@Launcher: 嗯,对内核对象的操作也有可能。

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 13:40

@hailong: 你的代码咋写的,我管不着,我只是给你解释这个问题“BeginXXX里传入的callback会在IOCP回调里被调用,即便里面有阻塞也是在IO线程吧,这会有影响?”的答案为什么是“有影响”。

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-07-14 13:41

@Launcher: 谢谢了!真是受益匪浅。另外问下,您主要在做C#开发吗?

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 13:42

@hailong: C++.

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-07-14 13:42

@Launcher: 好吧,我在做C++服务器开发,不过最近在看客户端代码,希望有所优化。谢谢您的帮助。

支持(0) 反对(0) hailong | 园豆:70 (初学一级) | 2014-07-14 13:43
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册