Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.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#问题中的多线程下载程序_C#_.net_Multithreading_Httpwebrequest_Httpwebresponse - Fatal编程技术网

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

C#问题中的多线程下载程序,c#,.net,multithreading,httpwebrequest,httpwebresponse,C#,.net,Multithreading,Httpwebrequest,Httpwebresponse,目前我有一个使用HttpWebRequest/Response的多线程下载程序类。一切正常,速度非常快,但是。。问题是数据需要在下载到另一个应用程序时进行流式传输。这意味着它必须以正确的顺序进行流式传输,首先是第一个块,然后是队列中的下一个块。当前我的downloader类是sync,Download()返回字节[]。例如,在我的异步多线程类中,我使用4个空元素(用于插槽)列出,并使用Download()函数将插槽的每个索引传递给每个线程。这模拟了同步,但这不是我需要的。我应该如何处理队列问题,

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

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

步骤:

  • 根据内容大小将您的下载分为多个块,或者每个线程下载的500KB左右的固定大小的内容下载程序
  • 启动线程时,指定块索引-第一部分、第二部分等
  • 下载可用时,根据区块索引对齐最终内容

  • 如果有兴趣,您可能想看一看(C,基于Linux的-at)或Axel的代码。

    能否显示下载代码以及启动多个异步线程的代码

    也许我没有完全理解您的场景,但如果我是您,我会使用Async(从responseStream开始)。然后我会做下面的

    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 {
         }
    }
    
    }
    

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

    其他下载线程也可以这样做,在完成时发出各自事件的信号。使用手动重置事件,这样即使没有人在等待事件,事件仍将保持信号状态。当需要数据块的线程完成处理第一个数据块时,它可以等待第二个事件。如果第二个事件已经发出信号,那么等待将立即返回,线程可以开始处理第二个数据块

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


    如果你聪明而谨慎,你可能只需要一个事件就可以完成这一切:创建一个数据块指针/对象的全局数组,初始设置为null,工作线程下载数据块,并将完成的数据块分配到全局数组中各自的槽位,然后向共享事件发送信号。使用者线程保留一个数据块计数器,以便它知道下一个需要处理的数据块,等待共享事件,并在发出信号时查看全局数组中的下一个插槽,以查看数据是否出现在那里。如果序列中的下一个插槽中仍然没有数据,则使用者线程返回等待事件。您还需要一种方法让工作线程知道下一步应该下载哪个数据块——一个受互斥体保护或使用Interlockedd/exchange访问的全局计数器就足够了。每个工作线程递增全局计数器并下载该数据块编号,并将结果分配到全局数据块列表中的第n个插槽。

    我已经这样做了。我有5个块(线程),每个1mb。但我只在5个线程全部完成时才输出数据,因为我不知道如何检查顺序是否紧凑。不需要等到5个线程全部完成。您还可以使用另一种策略—创建一个5MB大小的文件,当线程响应可用时,将其刷新到该文件中!:)是的,但我需要流式传输数据,而不是特别使用文件。这就是为什么我不能用这种方法。啊!使用内存映射文件和“最高连续块索引”如何。例如,在1,2,5个线程结束后,将索引值标记为2。下载3后,在下载4后将其设为3,将其设为5。这是我的代码:DownloadSingleChunk(索引、startPos、endPos),当它获得指定的范围时,将其写入chunkOutput列表。我想过使用计数器的方法,但这意味着我应该循环我的主线程,或者另一个工作线程。是吗?如果您的下载量大于几兆字节,是的,您应该让每个工作线程循环以拾取更多的块进行下载。您希望将线程数量保持在相当小的范围内,并防止每个下载块的大小变得过大。下载非常大的数据块时,遇到数据包丢失或传输错误的风险更大,这可能会迫使您重新启动整个数据块。在我看来,每个可以在一两分钟内传输的小数据块对网络错误更具弹性。是的,你应该有另一个线程来循环,等待数组中的下一个数据块可用,然后将它发送到你要发送的任何地方,在我的文章的最后称为“消费者线程”。您不希望主UI线程阻止等待事件,因为这将导致UI冻结。