Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 同时运行x个web请求_C#_Multithreading_Asynchronous_Webrequest - Fatal编程技术网

C# 同时运行x个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()

我们公司有一个web服务,我想通过我自己的
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来说,好的一点——有一段时间我为什么要实现我自己而不是使用它,这导致了我忘记它,但是是的,这是一个很好的解决方案。