开发者

Sequential Asynchronous Tasks Without Blocking

I have a C# WPF application using a rather noddy MVVM approach. In one of the ViewModels I would like to run a sequence of tasks sequentially but would like to run each asynchronous to the main thread. I want the granularity of being able to report progress between tasks but I don't want to block the GUI while any of the tasks is running.

Is there a standard way of achieving this, or a "best practice"?

I have implemented something that makes use of BackgroundWorker that I feel at once happy and mildly horrified with. The code to kick the whole thing off feels especially non-C#ish. I feel there must be a better or, at least, an established way of doing this.

Many thanks for your suggestions.

Dan


Here's the cobbled-together option:

protected void runAsyncTask(SequentialTask seqTask)
    {
        if (HasErrored) return;

        DoWorkEventHandler worker = (s, e) =>
        {
            setTaskStartStatusMessage(seqTask.TaskMessage);
            ShowProgress = true;
            seqTask.Task((BackgroundWorker)s);
        };

        ProgressChangedEventHandler progress = (s, e) =>
        {
            if (seqTask.TaskProgress != null)
                seqTask.TaskProgress(e.ProgressPercentage, e.UserState);
        };

        RunWorkerCompletedEventHandler done = null;
        done = (s, e) =>
        {
            ShowProgress = false;
            if (e.Error != null)
            {
                HasErrored = true;
                displayTaskExceptionMessage(e.Error, seqTask.TaskMessage);
            }
            else
            {
                setTaskCompleteStatusMessage(seqTask.TaskMessage);
       开发者_StackOverflow         if (seqTask.TaskComplete != null)
                    seqTask.TaskComplete();
            }
            ((BackgroundWorker)s).RunWorkerCompleted -= done;
            ((BackgroundWorker)s).DoWork -= worker;
            ((BackgroundWorker)s).ProgressChanged -= progress;

            if (seqTask.NextTask != null && (seqTask.CanExecuteNext == null ? true : seqTask.CanExecuteNext()))
                runAsyncTask(seqTask.NextTask);
        };

        if (seqTask.TaskProgress != null)
            backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.DoWork += worker;
        backgroundWorker.RunWorkerCompleted += done;
        backgroundWorker.ProgressChanged += progress;
        backgroundWorker.RunWorkerAsync();
    }

SequentialTask is just a simple set of Properties:

public class SequentialTask
{
    public Action<BackgroundWorker> Task { get; set; }
    public String TaskMessage { get; set; }
    public Func<bool> CanExecuteNext { get; set; }
    public Action<int, object> TaskProgress { get; set; }
    public Action TaskComplete { get; set; }
    public SequentialTask NextTask { get; set; }
}

Which leads to the Perl-like syntax of:

runAsyncTask(new SequentialTask()
        {
            Task = (x) => loadFile(),
            TaskMessage = "Load File",
            CanExecuteNext = null,
            NextTask = new SequentialTask()
            {
                Task = (x) => validateImport(),
                TaskMessage = "Validate Input Lines",
                TaskComplete = () =>
                {
                    if (!ImportIsValid)
                        displayValidationMessages();
                },
                CanExecuteNext = () => ImportIsValid,
                NextTask = new SequentialTask()
                {

etc.


Have you looked at the Task Parallel Library (TPL) in .NET 4.0? This allows you to do things like this:

Task firstTask = new Task(()=>RunStepOne());
firstTask.ContinueWith(task=>()=>RunSecondStep());
firstTask.Start();

There are a ton of options for creating, continuing, and stopping tasks built on the TPL. It will certainly be worth a look.


I don't see anything wrong with your approach. And the most important thing is: it works.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜