Question

I have a Task structure that is a little bit complex(for me at least). The structure is:

(where T = Task)

T1, T2, T3... Tn. There's an array (a list of files), and the T's represent tasks created for each file. Each T has always two subtasks that it must complete or fail: Tn.1, Tn.2 - download and install. For each download (Tn.1) there are always two subtasks to try, download from two paths(Tn.1.1, Tn.1.2).

Execution would be:

First, download file: Tn1.1. If Tn.1.1 fails, then Tn.1.2 executes. If either from download tasks returns OK - execute Tn.2. If Tn.2 executed or failed - go to next Tn.

I figured the first thing to do, was to write all this structure with jagged arrays:

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

So now I got my mainTask array of tasks, containing nicely ordered tasks just as described above. However after reading the docs on ContinuationTasks, I realise this does not help me since I must call e.g. Task.ContinueWith(Task2). I'm stumped about doing this on my mainTask array. I can't write mainTask[0].ContinueWith(mainTask[1]) because I dont know the size of the array.

If I could somehow reference the next task in the array (but without knowing its index), but cant figure out how. Any ideas? Thank you very much for your help.

Regards,

Was it helpful?

Solution

If you want to run some operations in sequence on a background thread, you don't need lots of Tasks, you need just one:

Task.Factory.StartNew(DownloadAndInstallFiles)

And inside that Task, you can use normal sequential control flow constructs, like foreach and ifs to do what you want. Something like:

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

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

OTHER TIPS

What about using the Parallel library on a List of queued apps to install?

As svick says in the comments above, the process of trying to download then install is very synchronous, so you would not need tasks for that but just some sequential code.

But for downloading all apps and install in parallel at the same time, you may want to fill a List of apps with your data and download + install methods, then just use the Parallel library to exectue all in parallel. Here is an example, I guessed a lot of things from your post but should give you an idea of what I'm talking about.

If you wish to also have the whole thing in a separate task and do other things while all apps download in parallel, you can enclose the Parallel code in a task and execute it somewhere else in your code, continue woorking on other things then finally wait for the task to finish.

This could be a class for each App you want to download:

/// <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;
    }
}

Then here is the code that will download and install all apps in parallel. You can also use an instance of the class ParallelOptions to personalize how the Parallel loop works.

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

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

// Try Install all Apps in parallel
Parallel.ForEach(queuedApps, (app) => app.InstallApp());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top