为啥 IOCP 在 BeginExecuteReader 中不起作用
Posted
技术标签:
【中文标题】为啥 IOCP 在 BeginExecuteReader 中不起作用【英文标题】:Why IOCP doesn't work in BeginExecuteReader为什么 IOCP 在 BeginExecuteReader 中不起作用 【发布时间】:2012-03-31 07:35:33 【问题描述】:我读过很多文章说 IOCP 用于 BeginXX/EndXX 对调用。 但是,当我测试它们时,我的结果显示 IOCP 在 BeginExecuteReader 调用中不起作用,而在 BeginGetResponse 调用中却可以正常工作。
我对这个结果感到非常困惑。谁能告诉我原因?我的测试代码有什么问题吗?
这是下面的测试:
使用 BeginGetResponse 进行测试
代码:
public static void IoThread2()
ThreadPool.SetMinThreads(5, 3);
ThreadPool.SetMaxThreads(5, 3);
int w; int io;
ThreadPool.GetAvailableThreads(out w, out io);
int cid = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Begin:" + w.ToString() + ";" + io.ToString() + "; id = " + cid.ToString());
ManualResetEvent waitHandle = new ManualResetEvent(false);
WebRequest request = HttpWebRequest.Create("http://www.cnblogs.com/");
AsyncCallback cb = new AsyncCallback(IOThread2CallBack);
request.BeginGetResponse(cb, request);
waitHandle.WaitOne();
public static void IOThread2CallBack(IAsyncResult ar)
try
WebRequest request = (WebRequest)ar.AsyncState;
int w2; int io2;
ThreadPool.GetAvailableThreads(out w2, out io2);
int cid2 = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("End:" + w2.ToString() + ";" + io2.ToString() + "; id = " + cid2.ToString());
var response = request.EndGetResponse(ar);
catch (Exception ex)
结果:
Begin:5;3; id = 10
End:5;2; id = 13
使用一个 IO 线程执行回调。
使用 BeginExecuteReader 进行测试 代码:
public static void IoThread1()
ThreadPool.SetMinThreads(5, 3);
ThreadPool.SetMaxThreads(5, 3);
int w; int io;
ThreadPool.GetAvailableThreads(out w, out io);
int cid = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Begin:" + w.ToString() + ";" + io.ToString() + "; id = " +
cid.ToString());
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
AsyncCallback da = new AsyncCallback(IoThreadCallBack);
SqlCommand command = new SqlCommand(s_QueryDatabaseListScript, connection);
IAsyncResult ir = command.BeginExecuteReader(da,
command,System.Data.CommandBehavior.CloseConnection);
ManualResetEvent waitHandle = new ManualResetEvent(false);
waitHandle.WaitOne();
public static void IoThreadCallBack(IAsyncResult ar)
int w; int io;
ThreadPool.GetAvailableThreads(out w, out io);
int cid = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("End:" + w.ToString() + ";" + io.ToString() + "; id = " +
cid.ToString());
SqlCommand command = (SqlCommand)ar.AsyncState;
StringBuilder sb = new StringBuilder();
try
using (SqlDataReader reader = command.EndExecuteReader(ar))
while (reader.Read())
sb.Append(reader.GetString(0)).Append("; ");
catch (Exception ex)
finally
command.Connection.Close();
结果:
Begin:5;3; id = 10
End:4;3; id = 7
另一个工作线程被用来执行回调
有什么问题?
【问题讨论】:
我很困惑...我们预计回调将在不同的线程上执行。这在很大程度上是使用 Begin*... 的全部意义?注意:使用 Begin/End 回调来打开一个等待句柄是没有意义的 - 如果你要在调用者处等待,你可能根本不使用 Begin*。 @MarcGravell,我认为问题是为什么在第二种情况下使用工作线程而不是 IOCP。 I/O 完成端口可用于简单的 I/O 请求,由重叠的 Read/WriteFile() 调用启动。 SQL I/O 请求没有什么简单的,中间有一大块软件,称为“SQL Native Client”。以及 System.Data.SqlClient 中的一大段 C++/CLI 代码。 TDS 是协议,SNI 是接口,通道可以命名为管道、套接字或共享内存。所有的文件都记录得很差,拆开眼睛看不到。 【参考方案1】:你的复制品很有创意。这是一个有趣的案例。
尝试使用命令文本“WAITFOR DELAY '02:00';”启动 100 个 SqlCommands并观察线程计数如何随着时间的推移而发展(启动一个打印统计信息的计时器)。
我的猜测是,我们将看到消耗的线程远少于 100 个(意味着这部分异步行为正在工作)。由于某种原因,SqlCommand 使用了“错误的”线程池线程,但这可能根本不会降低可伸缩性。
【讨论】:
我们观察到的只是你的回调在工作线程上执行。但是 IO 可能仍然作为 IOCP 执行。这就是重要的,因为它可以让您在没有数千个线程的情况下拥有数千个未完成的操作。请试试这个实验。以上是关于为啥 IOCP 在 BeginExecuteReader 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章