【问题标题】:Multithreaded Uploader多线程上传器
【发布时间】:2013-08-13 23:45:02
【问题描述】:

我想将大文件(2 到 40 GB)上传到 Azure Blob 存储。

首先,我成功地将每个文件拆分为块(每个块 = 2 MB)。 然后我一个一个地上传块,在每个块上传成功后,我更新一个临时文件,以便在应用程序关闭时能够恢复上传。

现在我想让上传操作多线程。 在查看了 TPL 之后,我很困惑从哪里开始!

任何从 TPL 开始的指导?

void Upload(int segmentId)
{
    try
    {
        string blockId = GetBlockId(segmentId);
        var segment = GetSegment(FilePath, segmentId, SeqmentSize);
        var md5Hash = CalcMd5Hash(segment);

        var blob = new CloudBlockBlob(_link.Uri);
        using (var memoryStream = new MemoryStream(segment))
        {
            blob.PutBlock(blockId, memoryStream, md5Hash);
        }

        SerializeStatus();
    }
    catch (Exception exception)
    {
        ...
    }
}

【问题讨论】:

  • 请提供一些代码
  • 您的可用上传带宽是多少??
  • @RobertoBonini ~ 8 Mbit
  • 如果segment 3在segment 2之前被上传,那会被服务器上的blockId处理吗?
  • @Belogix 我有一个方法来处理段的顺序

标签: c# multithreading azure task-parallel-library


【解决方案1】:

我很久以前就在我想要upload really large blobs with resumable capability的地方构建了类似的东西(尽管我使用的是异步方法而不是 TPL)。这就是我所做的:

  1. 首先根据块大小,我将文件分成块。每个块都分配有一个 id。然后我创建了一个对象,该对象包含块 ID 和该块的状态。为简单起见,我为一个块保留了以下状态 - NotStartedSuccessfulFailed
  2. 然后,我创建了这些块的集合,并将这些数据序列化到一个文件中。
  3. 基于并行线程的数量(假设为 x),我从状态为 NotStarted 的集合中获取 x 个项目。然后我并行处理这些块。我将块 ID 作为用户状态传递,这样当我收到回调时,根据上传状态,我会相应地更新集合并将数据序列化。
  4. 一旦所有区块都上传完毕,我会检查是否有任何失败的区块。如果有,我会重试这些块。
  5. 一旦所有块都成功完成,我只需从块集合中创建一个阻止列表并提交该阻止列表。如果提交阻止列表操作成功,我只是删除了包含块数据的文件。

希望这会有所帮助。

更新

看看这个伪代码,看看它是否对你有帮助:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UploadLargeBlob
{
    class Program
    {
        static void Main(string[] args)
        {
            List<ChunkInformation> chunksToUpload = new List<ChunkInformation>();
            CreateChunkCollection("MyVeryLargeFile", 2*1024*1024);
            int numberOfParallelThreads = 8;
            do
            {
                var chunksToProcess = chunksToUpload.Where(c => c.Status == ChunkStatus.NotStarted || c.Status == ChunkStatus.Failed).Take(numberOfParallelThreads);
                if (chunksToProcess.Count() == 0)
                {
                    break;
                }
                List<Task> tasks = new List<Task>();
                try
                {
                    foreach (var chunk in chunksToProcess)
                    {
                        tasks.Add(Task.Factory.StartNew(() =>
                            {
                                DoUpload(chunk);
                            }, chunk));
                    }
                    Task.WaitAll(tasks.ToArray());
                }
                catch (AggregateException excep)
                {
                    foreach (var task in tasks)
                    {
                        if (task.Exception != null)
                        {
                            ChunkInformation chunk = task.AsyncState as ChunkInformation;
                            chunk.Status = ChunkStatus.Failed;
                            //Now serialize the data.
                        }
                    }
                }
            }
            while (true);
        }

        static void DoUpload(ChunkInformation chunk)
        {
            //Do the actual upload

            //Update chunk status once chunk is uploaded
            chunk.Status = ChunkStatus.Successful;

            //Serialize the data.
        }

        static void CreateChunkCollection(string fileName, int chunkSize)
        {
        }
    }

    public class ChunkInformation
    {
        public string Id
        {
            get;
            set;
        }

        public ChunkStatus Status
        {
            get;
            set;
        }
    }

    public enum ChunkStatus
    {
        NotStarted,
        Successful,
        Failed
    }
}

【讨论】:

  • 这正是我所做的。我的问题是如何做这个多线程?
  • 这是多线程的。 Task.Factory.StartNew 将一个新的工作项添加到 TPL 的线程池工作项队列中。
猜你喜欢
  • 1970-01-01
  • 2016-11-26
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
相关资源
最近更新 更多