C#问题中的多线程下载器

时间:2010-05-06 10:43:08

标签: c# .net multithreading httpwebrequest httpwebresponse

目前我有使用HttpWebRequest / Response的多线程下载器类。一切正常,它超级快,但是...问题是数据需要在下载到另一个应用程序时进行流式处理。这意味着必须以正确的顺序进行流式处理,首先是第一个块,然后是队列中的下一个。目前我的下载程序类是sync,而Download()返回byte []。在我的async多线程类中,例如,列出了4个空元素(用于插槽),我使用Download()函数将每个插槽的索引传递给每个线程。这模拟了同步,但这不是我需要的。我应该如何处理队列,以确保在第一个块开始下载后立即流式传输数据。

3 个答案:

答案 0 :(得分:2)

如果您的问题是如何确定哪个线程正在下载第一个块以及第一个块何时可以使用,请使用每个线程的事件并跟踪您为哪些线程分配的块。跟踪您传递给第一个线程的事件(将下载第一个数据块),传递给第二个线程的事件(第二个数据块)等。具有主线程或其他后台线程(为了避免阻塞UI线程),等待第一个事件。当第一个线程完成下载其块时,它设置/发信号通知第一个事件。等待的线程将被唤醒,并可以使用第一块数据。

其他下载线程可以执行相同的操作,在完成后发出相应的事件信号。使用手动重置事件,即使没有人在等待事件,事件仍将保持信号状态。当需要数据块的线程完成处理第一个数据块时,它可以等待第二个事件。如果第二个事件已经发出信号,则等待将立即返回,并且线程可以开始处理第二个数据块。

对于非常大的下载,您可以循环方式重用事件和线程。只要消耗数据块的线程按顺序使用它们并按顺序等待各自的事件,它们完成的顺序就不重要了。

如果你聪明而小心,你可以只使用一个事件来完成所有这些:创建一个数据块指针/对象的全局数组,最初设置为null,工作线程下载数据块并将完成的块分配给它们各自在全局数组中的插槽然后发出共享事件的信号。消费者线程保持数据块计数器,以便它知道接下来需要处理哪个数据块,等待共享事件,以及何时发信号通知查看全局数组中的下一个槽以查看数据是否出现在那里。如果顺序中的下一个插槽中仍然没有数据,则消费者线程将返回等待事件。您还需要一种让工作线程知道接下来应该下载哪个数据块的方法 - 由互斥锁保护或使用interlockedadd / exchange访问的全局计数器就足够了。每个工作线程递增全局计数器并下载该数据块编号,并将结果分配给全局数据块列表中的第n个插槽。

答案 1 :(得分:0)

要创建同步的多线程下载器,您需要创建正确的数据结构,并且您需要的不仅仅是byte[]数据。

<强>步骤:

  1. 根据每个帖子下载的内容大小或固定大小的内容下载程序将下载分为多个块。
  2. 启动线程时,请指定块索引 - 第1部分,第2部分等
  3. 下载可用时,请根据块索引对齐最终内容。
  4. 如果有兴趣,您可能需要查看prozilla(C,Linux based-at)或Axel的代码。

答案 2 :(得分:0)

您是否可以显示执行下载的代码以及启动多个异步线程的代码?

也许我不完全理解你的场景,但如果我是你,我会在responseStream上使用Async(BeginRead)。然后我会做以下事情....

void StartReading(Stream responseStream)
{
    byte [] buffer = new byte[1024];
    Context ctx = new Context();
    ctx.Buffer = buffer;
    ctx.InputStream = responseStream;
    ctx.OutputStream = new MemoryStream(); // change this to be your output stream

    responseStream.BeginRead(buffer, 0, buffer.Length; new AsyncCallback(ReadCallback), ctx);
}

void ReadCallback(IAsyncResult ar)
{
    Context ctx = (Context)ar.AsyncState;
    int read = 0;
    try {
        read = ctx.InputStream.EndRead(ar);
        if (read > 0)
        {
            ctx.OutputStream.Write(ctx.Buffer, 0, read);
            // kick off another async read
            ctx.InputStream.BeginRead(ctx.Buffer, 0, ctx.Buffer.Length, new AsyncCallback(ReadCallback), ctx);
        } else {
            ctx.InputStream.Close();
            ctx.OutputStream.Close();
        }
     } catch {
     }
}

}