【问题标题】:chaining array of tasks with continuation将任务数组与 continuation 链接起来
【发布时间】:2012-07-01 20:38:39
【问题描述】:

我有一个有点复杂的任务结构(至少对我来说)。结构是:

(其中 T = 任务)

T1、T2、T3...Tn。有一个数组(文件列表),T 代表为每个文件创建的任务。 每个 T 始终有两个必须完成或失败的子任务:Tn.1、Tn.2 - 下载和安装。 对于每次下载(Tn.1),总是有两个子任务要尝试,从两个路径(Tn.1.1,Tn.1.2)下载。

执行将是:

首先,下载文件:Tn1.1。如果 Tn.1.1 失败,则 Tn.1.2 执行。 如果任一下载任务返回 OK - 执行 Tn.2。 如果 Tn.2 执行或失败 - 转到下一个 Tn。

我想到要做的第一件事就是用锯齿状数组编写所有这些结构:

    private void CreateTasks()
    {     
        //main array
        Task<int>[][][] mainTask = new Task<int>[_queuedApps.Count][][];
        for (int i = 0; i < mainTask.Length; i++)
        {
            Task<int>[][] arr = GenerateOperationTasks();
            mainTask[i] = arr;
        }
    }

    private Task<int>[][] GenerateOperationTasks()
    {
        //two download tasks 
        Task<int>[] downloadTasks = new Task<int>[2];
        downloadTasks[0] = new Task<int>(() => { return 0; });
        downloadTasks[1] = new Task<int>(() => { return 0; });

        //one installation task 
        Task<int>[] installTask = new Task<int>[1] { new Task<int>(() => { return 0; }) };

        //operations Task is jagged - keeps tasks above
        Task<int>[][] operationTasks = new Task<int>[2][];
        operationTasks[0] = downloadTasks;
        operationTasks[1] = installTask;

        return operationTasks;
    }

所以现在我得到了我的 mainTask 任务数组,其中包含如上所述的有序任务。但是,在阅读了有关 ContinuationTasks 的文档后,我意识到这对我没有帮助,因为我必须调用例如Task.ContinueWith(Task2)。我很难在我的 mainTask 数组上这样做。我不能写 mainTask[0].ContinueWith(mainTask[1]) 因为我不知道数组的大小。

如果我能以某种方式引用数组中的下一个任务(但不知道它的索引),但不知道如何。 有任何想法吗?非常感谢您的帮助。

问候,

【问题讨论】:

  • 您不会将Task 与另一个已经创建的Task 一起继续。相反,ContinueWith() 接受一个委托并返回延续 Task
  • 另外,我完全不明白你为什么要使用Tasks。您描述的工作流程是完全同步的,您不需要Tasks。
  • 感谢您的澄清,svick。我使用任务是因为有人建议我在这里这样做:stackoverflow.com/questions/11145938/…。如果不是任务,您能否建议我还可以使用什么来完成我提到的行为?一种在单独线程上处理一组操作并在执行结束时获取结果/失败列表的机制。谢谢

标签: .net c#-4.0 task-parallel-library continuations


【解决方案1】:

如果你想在后台线程上按顺序运行一些操作,你不需要很多Tasks,你只需要一个:

Task.Factory.StartNew(DownloadAndInstallFiles)

Task 内部,您可以使用普通的顺序控制流结构,如foreachifs 来做您想做的事。比如:

void DownloadAndInstallFiles()
{
    foreach (var file in files)
    {
       var downloaded = Download(file, primarySource);
       if (downloaded == null)
           downloaded = Download(file, secondarySource);

       if (downloaded != null)
           Install(downloaded);
    }
}

【讨论】:

  • 是的,看来我把事情复杂化了,就像用推土机捡花生一样。我也想过这样做,但我认为多个任务可能在报告完成状态方面具有优势。我会走这条路,谢谢。
  • 嗯,这取决于你到底想做什么。但是如果你只想在最后报告每个文件的结果,我认为自定义类型会比Task更好。
【解决方案2】:

在要安装的排队应用列表中使用并行库怎么样?

正如 svick 在上面的 cmets 中所说,尝试下载然后安装的过程非常同步,因此您不需要为此执行任务,而只需要一些顺序代码。

但要同时下载所有应用程序并并行安装,您可能需要用您的数据和下载+安装方法填写应用程序列表,然后只需使用并行库即可并行执行所有应用程序。这是一个例子,我从你的帖子中猜到了很多东西,但应该让你知道我在说什么。

如果您还希望将整个内容放在单独的任务中并在所有应用程序并行下载时执行其他操作,您可以将并行代码包含在任务中并在代码中的其他位置执行它,继续处理其他事情然后最后等待任务完成。

这可能是您要下载的每个应用程序的一个类:

/// <summary>
/// Class for each of your apps to be downloaded and installed. You can also
/// derive this in other types of apps that require a different download or install
/// approach.
/// </summary>
class AppToDownload
{
    /// <summary>
    /// The addresses to download this app
    /// </summary>
    public List<string> DownloadAddresses { get; set; }

    /// <summary>
    /// Flag that tells if this task was successfully installed. Set to false
    /// as default.
    /// </summary>
    public bool Installed { get; set; }

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="downloadAddresses">Addresses to download this app from, they
    /// should be in order of preference</param>
    public AppToDownload(List<string> downloadAddresses)
    {
        this.DownloadAddresses = downloadAddresses;
        this.Installed = false;
    }

    /// <summary>
    /// Public method to be called to Install this app. It will download, and if
    /// successful, will install
    /// </summary>
    /// <returns></returns>
    public bool InstallApp()
    {
        this.Installed = (this.TryDownload() && this.TryInstall());
        return this.Installed;
    }

    /// <summary>
    /// Internal method to download this App. You can override this in a derived
    /// class if you need to.
    /// </summary>
    /// <returns></returns>
    protected virtual bool TryDownload()
    {
        bool res = false;

        // (...) Your code to download, return true if successful

        return res;
    }

    /// <summary>
    /// Internal method to install this App. You can override this in a derived
    /// class if you need to.
    /// </summary>
    /// <returns></returns>
    protected virtual bool TryInstall()
    {
        bool res = false;

        // (...) Your code to install, return true if successful

        return res;
    }
}

下面是并行下载和安装所有应用程序的代码。您还可以使用 ParallelOptions 类的实例来个性化 Parallel 循环的工作方式。

List<AppToDownload> queuedApps = new List<AppToDownload>();

// (...) Fill the list of apps

// Try Install all Apps in parallel
Parallel.ForEach(queuedApps, (app) => app.InstallApp());

【讨论】:

  • 谢谢,但我不应该在我的项目中并行执行多个操作。因为我没有提到,+1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-24
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多