Frank M
Frank M

Reputation: 533

asp.net Program execution continues before tasks finish executing

I am trying to run 3 of tasks on different threads (there will be a few more added.) The tasks that are called then call other tasks that are async / await.

The program execution continues after my command to wait. Execution needs to wait until all tasks are complete. My code is below (the null return is just there to test, I still need to create the return code.

public List<string> CopyFilesAsync(List<ModelIterationModel> model)
{
    var copyFileTaskParameters = GetCopyFileTaskParameters(model);
    Task<List<CopyFitDataResult>> fitDataResulLits = null;
    Task<List<CopyNMStoreResult>> nmStoreResultsList = null;
    Task<List<CopyDecompAnalyzerResult>> decompAnalyzerStoreResultsList = null;
    Task parent = Task.Factory.StartNew(() =>
    {
        var cancellationToken = new CancellationToken();
        TaskFactory factory = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously);
        factory.StartNew(() => fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken));
        factory.StartNew(() => decompAnalyzerStoreResultsList = CopyDecompAnalyzerFiles(copyFileTaskParameters, cancellationToken));
        factory.StartNew(() => nmStoreResultsList = CopyNMStoreResultsFiles(copyFileTaskParameters, cancellationToken));
    });
    parent.Wait();

    return null;
}

The calling code is synchronous. Execution continues in this method before the tasks above complete.

public void CreateConfigFile(CreateConfigFileParameter parameter)
{
    try
    {
        //data for this will need to come from UI, return values will include local file paths. All copy operations will be ran on it's own thread
        //return value will include local file paths
        var userFileListModel = _copyFilesToLocalDirectoryService.CopyFilesAsync(temp);

        //will return object graph of data read from speadsheets and excel files
        _readLocalFilesToDataModelService.ReadAllFiles();

        //will take object graph and do date period logic and event type compression and any other business
        //logic to extract an object model to create the config file
        _processDataModelsToCreateConfigService.Process();

        //will take extracted object model and use config file template to create the config file workbook
        _writeConfigFileService.WriteConfigFile();
    }
    catch(Exception ex)
    {

    }
}

This code is in a class library in a WPF application. I don't know if that is important, but this is the first time I have had to interact with WPF (15 years of web development only.)

What do I need to do to stop execution until all tasks have completed? I played around with a few other approaches, such as attaching as children but nothing I do seems to work.

Edit - I keep trying approaches straight out of MSDN samples with no luck whatsoever. Just tried this

var cancellationToken = new CancellationToken();
var tasks = new List<Task>();
tasks.Add(Task.Run(() =>
{
    fitDataResulLits =  CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
}));

Task t = Task.WhenAll(tasks.ToArray());
t.Wait();

Exactly like the MSDN sample, and I tried WaitAll but it runs right past it.
Could this have something to do with the Visual Studio debugger?

Upvotes: 0

Views: 87

Answers (1)

VMAtm
VMAtm

Reputation: 28355

There are many questions to your code:

  1. If you do not wait for files to be copied, how next lines of code should run?
  2. Why do you need to create a TaskFactory to start a background work, which is already a Task?
  3. Why do you create a CancellationToken? You need to create a CancellationTokenSource, and use it's Token for all your code you may need to cancel.

Also, this code:

tasks.Add(Task.Run(() =>
{
    fitDataResulLits =  CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
}));

doesn't fire the CopyFitDataFiles, it simply assigns a task reference. You need to do this:

tasks.Add(CopyFitDataFiles(copyFileTaskParameters, cancellationToken));

Your code should be rewritten in this way:

public async Task<List<string>> CopyFilesAsync(List<ModelIterationModel> model)
{
    var copyFileTaskParameters = GetCopyFileTaskParameters(model);
    // do not await tasks here, just get the reference for them
    var fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
    // ...

    // wait for all running tasks
    await Task.WhenAll(copyFileTaskParameters, ...);

    // now all of them are finished
}

// note sugnature change
public async Task CreateConfigFile
{
    // if you really need to wait for this task after some moment, save the reference for task
    var userFileListModel = _copyFilesToLocalDirectoryService.CopyFilesAsync(temp);

    ...

    // now await it
    await userFileListModel;

    ...
}

There is a great article about async/await: Async/Await - Best Practices in Asynchronous Programming by @StephenCleary

Upvotes: 1

Related Questions