C# 同时运行x个web请求
我们公司有一个web服务,我想通过我自己的C# 同时运行x个web请求,c#,multithreading,asynchronous,webrequest,C#,Multithreading,Asynchronous,Webrequest,我们公司有一个web服务,我想通过我自己的HTTPWebRequestclient在C#中发送XML文件(存储在我的驱动器上)。这已经奏效了。web服务同时支持5个同步请求(一旦服务器上的处理完成,我就会收到web服务的响应)。处理每个请求大约需要5分钟 抛出太多请求(>5)会导致我的客户端超时。此外,这可能会导致服务器端错误和数据不一致。在服务器端进行更改不是一个选项(来自不同的供应商) 现在,我的Webrequest客户端将使用result.AsyncWaitHandle.WaitOne()
HTTPWebRequest
client在C#中发送XML文件(存储在我的驱动器上)。这已经奏效了。web服务同时支持5个同步请求(一旦服务器上的处理完成,我就会收到web服务的响应)。处理每个请求大约需要5分钟
抛出太多请求(>5)会导致我的客户端超时。此外,这可能会导致服务器端错误和数据不一致。在服务器端进行更改不是一个选项(来自不同的供应商)
现在,我的Webrequest
客户端将使用result.AsyncWaitHandle.WaitOne()发送XML并等待响应代码>
然而,通过这种方式,一次只能处理一个请求,尽管web服务支持5。我尝试使用Backgroundworker
和Threadpool
,但它们同时创建了太多请求,这使它们对我毫无用处。有什么建议吗?如何解决这个问题?创建我自己的Threadpool
,正好有5个线程?有什么建议,如何实现吗?简单的方法是创建5个线程(除了:这是一个奇数!),它们使用BlockingCollection
中的xml文件
比如:
var bc = new BlockingCollection<string>();
for ( int i = 0 ; i < 5 ; i++ )
{
new Thread( () =>
{
foreach ( var xml in bc.GetConsumingEnumerable() )
{
// do work
}
}
).Start();
}
bc.Add( xml_1 );
bc.Add( xml_2 );
...
bc.CompleteAdding(); // threads will end when queue is exhausted
static void Main(string[] args)
{
var xmlFiles = new BlockingCollection<string>();
// Add some xml files....
SpawnThreads(5, () =>
{
using (var web = new WebClient())
{
web.UploadFile(xmlFiles.Take());
}
});
Console.WriteLine("Done");
Console.ReadKey();
}
var bc=new BlockingCollection();
对于(int i=0;i<5;i++)
{
新线程(()=>
{
foreach(bc.getconsumineGenumerable()中的var xml)
{
//工作
}
}
).Start();
}
添加(xml_1);
Add(xml_2);
...
bc.CompleteAdding();//当队列耗尽时,线程将结束
创建自己的五个线程的线程池并不复杂-只要创建一个描述请求的并发对象队列,并让五个线程根据需要循环执行任务即可。添加一个AutoResetEvent,您可以确保它们不会在没有需要处理的请求时疯狂旋转
但是,将响应返回到正确的调用线程可能很棘手。如果您的代码的其余部分是这样工作的,我将采用另一种方法,创建一个有点像监视器的限制器,但允许同时执行5个线程,而不是只执行一个线程:
private static class RequestLimiter
{
private static AutoResetEvent _are = new AutoResetEvent(false);
private static int _reqCnt = 0;
public ResponseObject DoRequest(RequestObject req)
{
for(;;)
{
if(Interlocked.Increment(ref _reqCnt) <= 5)
{
//code to create response object "resp".
Interlocked.Decrement(ref _reqCnt);
_are.Set();
return resp;
}
else
{
if(Interlocked.Decrement(ref _reqCnt) >= 5)//test so we don't end up waiting due to race on decrementing from finished thread.
_are.WaitOne();
}
}
}
}
私有静态类请求限制器
{
私有静态自动重置事件_are=新自动重置事件(false);
私有静态int_reqCnt=0;
公共响应对象请求(请求对象请求)
{
对于(;;)
{
if(Interlocked.Increment(ref _reqCnt)=5)//测试,这样我们就不会因为从完成的线程开始递减的竞争而结束等待。
_是。WaitOne();
}
}
}
}
您可以编写一个小助手方法,该方法将阻止当前线程,直到所有线程都执行完给定的动作委托
static void SpawnThreads(int count, Action action)
{
var countdown = new CountdownEvent(count);
for (int i = 0; i < count; i++)
{
new Thread(() =>
{
action();
countdown.Signal();
}).Start();
}
countdown.Wait();
}
静态空产卵线程(整数计数、操作)
{
var倒计时=新倒计时事件(计数);
for(int i=0;i
{
动作();
倒计时信号();
}).Start();
}
倒计时;
}
然后使用BlockingCollection
(线程安全集合)跟踪xml文件。通过使用上面的helper方法,您可以编写如下内容:
var bc = new BlockingCollection<string>();
for ( int i = 0 ; i < 5 ; i++ )
{
new Thread( () =>
{
foreach ( var xml in bc.GetConsumingEnumerable() )
{
// do work
}
}
).Start();
}
bc.Add( xml_1 );
bc.Add( xml_2 );
...
bc.CompleteAdding(); // threads will end when queue is exhausted
static void Main(string[] args)
{
var xmlFiles = new BlockingCollection<string>();
// Add some xml files....
SpawnThreads(5, () =>
{
using (var web = new WebClient())
{
web.UploadFile(xmlFiles.Take());
}
});
Console.WriteLine("Done");
Console.ReadKey();
}
static void Main(字符串[]args)
{
var xmlFiles=new BlockingCollection();
//添加一些xml文件。。。。
生成线程(5,()=>
{
使用(var web=new WebClient())
{
UploadFile(xmlFiles.Take());
}
});
控制台。写入线(“完成”);
Console.ReadKey();
}
更新
更好的方法是异步上传文件,这样就不会浪费资源在使用线程执行IO任务上
同样,您可以编写一个助手方法:
static void SpawnAsyncs(int count, Action<CountdownEvent> action)
{
var countdown = new CountdownEvent(count);
for (int i = 0; i < count; i++)
{
action(countdown);
}
countdown.Wait();
}
静态空产卵异步(整数计数、操作)
{
var倒计时=新倒计时事件(计数);
for(int i=0;i
然后像这样使用它:
static void Main(string[] args)
{
var urlXML = new BlockingCollection<Tuple<string, string>>();
urlXML.Add(Tuple.Create("http://someurl.com", "filename"));
// Add some more to collection...
SpawnAsyncs(5, c =>
{
using (var web = new WebClient())
{
var current = urlXML.Take();
web.UploadFileCompleted += (s, e) =>
{
// some code to mess with e.Result (response)
c.Signal();
};
web.UploadFileAsyncAsync(new Uri(current.Item1), current.Item2);
}
});
Console.WriteLine("Done");
Console.ReadKey();
}
static void Main(字符串[]args)
{
var urlXML=new BlockingCollection();
添加(Tuple.Create(“http://someurl.com“,”文件名“);
//添加一些更多到集合。。。
产卵异步(5,c=>
{
使用(var web=new WebClient())
{
var current=urlXML.Take();
web.UploadFileCompleted+=(s,e)=>
{
//一些代码会干扰e。结果(响应)
c、 信号();
};
UploadFileAsyncAsync(新Uri(current.Item1)、current.Item2);
}
});
控制台。写入线(“完成”);
Console.ReadKey();
}
如果您使用的是.Net 4,那么它看起来非常适合您。您可以设置其MaxDegreeOfParallelism
,这意味着您可以保证一次不会处理更多的项目
Parallel.ForEach(items,
new ParallelOptions { MaxDegreeOfParallelism = 5 },
ProcessItem);
这里,ProcessItem
是一种通过访问服务器并阻塞直到处理完成来处理一个项目的方法。如果您愿意,您可以使用lambda。只是吹毛求疵:我认为而(true)
表达“无限”循环的意图比(;;)
更好。特别是对于新手来说,你的方法可能会让人困惑。此外,你可以使用框架中的信号量(或信号量lim
)。@svick好吧,我们中的很多人从K&R那里学会了(;;)的成语,并将其理解为“永远”。把它看作是对Dennis Ritchie的敬意:)对SemaphoreSlim来说,好的一点——有一段时间我为什么要实现我自己而不是使用它,这导致了我忘记它,但是是的,这是一个很好的解决方案。