我有一个程序,我需要在外部sharepoint站点创建一些大量的文件夹(外部意思是我不能使用sharepoint对象模型)。 Web请求适用于此,但只需一次执行一个(发送请求,等待响应,重复)相当慢。我决定多线程化请求,尝试加快速度。该程序已大大加快,但经过一段时间(大约1-2分钟左右),并发异常开始被抛出。
代码如下,这是最好的解决方法吗?
Semaphore Lock = new Semaphore(10, 10);
List<string> folderPathList = new List<string>();
//folderPathList populated
foreach (string folderPath in folderPathList)
{
Lock.WaitOne();
new Thread(delegate()
{
WebRequest request = WebRequest.Create(folderPath);
request.Credentials = DefaultCredentials;
request.Method = "MKCOL";
WebResponse response = request.GetResponse();
response.Close();
Lock.Release();
}).Start();
}
for(int i = 1;i <= 10;i++)
{
Lock.WaitOne();
}
例外是
未处理的异常:System.Net.WebException:无法连接到远程服务器---&gt; System.Net.Sockets.SocketException:通常只允许每个套接字地址使用一次192.0.0.1:81
在System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot,SocketAddre
ss socketAddress)
在System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP)
在System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket&amp; socket,IPAddress&amp; address,ConnectSocketState state,
IAsyncResult asyncResult,Int32 timeout,Exception&amp;例外)
答案 0 :(得分:19)
您可能会创建太多连接,从而耗尽您可以使用的所有本地端口。
关闭端口后可以重新使用端口的超时时间。
WebRequest
隐藏了所有低级套接字处理,但我猜它最终用完了端口,或者尝试(重新)绑定到已经处于TIME_WAIT状态的套接字。
即使您不关心响应,也应确保阅读响应流。这应该有助于不产生太多延迟的连接。
WebResponse response = request.GetResponse();
new StreamReader(response.GetResponseStream()).ReadToEnd();
我会粘贴here的一些相关信息:
当连接关闭时,在关闭连接的一侧是5元组 {Protocol,Local IP,Local Port,Remote IP,Remote Port}默认进入TIME_WAIT状态240秒。 在这种情况下,协议是固定的 - TCP 本地IP,远程IP和远程PORT通常也是固定的。所以变量是本地端口。 发生的情况是,当您不绑定时,使用1024-5000范围内的端口。 所以粗略地说你有4000个端口。如果你在4分钟内使用所有这些 - 大致意味着你 每秒进行16次Web服务调用,持续4分钟,您将耗尽所有端口。这就是这个例外的原因。
现在好了如何解决这个问题?
其中一种方法是增加动态端口范围。默认情况下,最大值为5000.您可以将其设置为65534。 HKLM \ System \ CurrentControlSet \ Services \ Tcpip \ Parameters \ MaxUserPort是使用的关键。
您可以做的第二件事是,一旦连接进入TIME_WAIT状态,您可以减少它的时间 在该状态下,默认值为4分钟,但您可以将其设置为30秒 HKLM \ System \ CurrentControlSet \ Services \ Tcpip \ Parameters \ TCPTimedWaitDelay是使用的密钥。 将此设置为30秒
答案 1 :(得分:10)
您没有关闭可能导致连接长时间打开的webrequest。对于Parallel.Net的Parallel.Foreach来说,这听起来是一个完美的工作,只需要确定你希望它运行多少线程
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 10;
Parallel.ForEach(folderPathList, parallelOptions, folderPathList =>
{
using(WebRequest request = WebRequest.Create(folderPath))
{
request.Credentials = DefaultCredentials;
request.Method = "MKCOL";
GetResponse request = WebRequest.Create(folderPath);
request.Credentials = DefaultCredentials;
request.Method = "MKCOL";
using (WebResponse response = request.GetResponse());
}
});
要记住的另一件事是maxConnections,请务必在app.config中设置它:
<configuration>
<system.net>
<connectionManagement>
<add address = "*" maxconnection = "100" />
</connectionManagement>
</system.net>
</configuration>
在现实世界的场景中,您必须添加try-catch并重试可能超时的连接,从而导致更复杂的代码
答案 2 :(得分:2)
对于这种IO密集型任务,asynchronous programming model非常有用。但是,它在C#中有点难以使用.C#现在也支持异步语言,你可以试试CTP release。
答案 3 :(得分:1)
试试这个
folderPathList.ToList().ForEach(p =>
{
ThreadPool.QueueUserWorkItem((o) =>
{
WebRequest request = WebRequest.Create(p);
request.Credentials = DefaultCredentials;
request.Method = "MKCOL";
WebResponse response = request.GetResponse();
response.Close();
});
编辑 - 不同的webrequest方法
folderPathList.ToList().ForEach(p =>
{
ThreadPool.QueueUserWorkItem((o) =>
{
using (WebClient client = new WebClient())
{
client.Credentials = DefaultCredentials;
client.UploadString(p, "MKCOL", "");
}
});
});