继续执行多项任务

发布于 2024-12-02 23:35:02 字数 1286 浏览 5 评论 0原文

我正在使用 TPL 在 MVVM 应用程序的后台线程上串行执行两个任务。任务运行时,应用程序会显示“进度”对话框。因此,我的 MVVM 命令的 Execute() 方法首先在主视图模型中引发一个 ImageProcessingStarting 事件。视图通过显示“进度”对话框来响应事件。然后,该命令启动第一个任务,继续执行第二个任务,并通过在主视图模型中引发 ImageProcessingEnding 事件来执行最终的“继续”。视图通过关闭“进度”对话框来响应该事件。代码如下。

两个后台任务都正常执行,但“进度”对话框在第一个任务完成后(而不是第二个任务完成后)提前关闭。我希望有人能告诉我原因以及如何解决这个问题。感谢您的帮助。


public void Execute(object parameter)
{
    ...

    // Announce that image processing is starting
    m_ViewModel.RaiseImageProcessingStartingEvent();

    // Set up a cancellation token source
    var tokenSource = new CancellationTokenSource();
    m_ViewModel.ProgressDialogViewModel.TokenSource = tokenSource;

    // Background Task #1: Add time stamps to files
    var task = Task.Factory.StartNew(() => this.AddTimeStampsToFiles(fileList, progressDialogViewModel));

    /* The Progress dialog is closing at this point! */

    // Background Task #2: Resequence files
    task.ContinueWith(t => this.ResequenceFiles(fileList, progressDialogViewModel));

    /* The Progress dialog should close at this point. */

    // Announce that image processing is finished
    task.ContinueWith(t => m_ViewModel.RaiseImageProcessingEndingEvent(), TaskScheduler.FromCurrentSynchronizationContext());
}

I am using TPL to perform two tasks serially on a background thread in an MVVM app. While the tasks are running, the app displays a Progress dialog. So, my MVVM command's Execute() method first raises an ImageProcessingStarting event in the main view model. The view responds to the event by displaying the Progress dialog. The command then launches the first task, continues with the second task, and performs the final 'continue with' by raising an ImageProcessingEnding event in the main view model. The view responds to the event by closing the Progress dialog. The code is below.

Both background tasks are executing properly, but the Progress dialog is closing early, after the first task completes, instead of after the second one. I am hoping someone can tell me why, and how to fix the problem. Thanks for your help.


public void Execute(object parameter)
{
    ...

    // Announce that image processing is starting
    m_ViewModel.RaiseImageProcessingStartingEvent();

    // Set up a cancellation token source
    var tokenSource = new CancellationTokenSource();
    m_ViewModel.ProgressDialogViewModel.TokenSource = tokenSource;

    // Background Task #1: Add time stamps to files
    var task = Task.Factory.StartNew(() => this.AddTimeStampsToFiles(fileList, progressDialogViewModel));

    /* The Progress dialog is closing at this point! */

    // Background Task #2: Resequence files
    task.ContinueWith(t => this.ResequenceFiles(fileList, progressDialogViewModel));

    /* The Progress dialog should close at this point. */

    // Announce that image processing is finished
    task.ContinueWith(t => m_ViewModel.RaiseImageProcessingEndingEvent(), TaskScheduler.FromCurrentSynchronizationContext());
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

允世 2024-12-09 23:35:02

根据 MSDN

Task.ContinueWith 方法

创建一个当目标执行时异步执行的延续
任务完成。

这意味着当主要任务完成时,两个ContinueWith 调用中的其他项目将并行运行。

为了演示这一点,您可以使用下面的代码:

System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(() => Console.WriteLine("1"));
task.ContinueWith((t) => { System.Threading.Thread.Sleep(1000); Console.WriteLine("2"); });
task.ContinueWith((t) => Console.WriteLine("3"));

然后输出窗口将显示:

1
3
2

为了帮助您解决问题,我一直使用 System.ComponentModel.BackgroundWorker 来串行运行任务。可能有更好的方法,但这目前对我有用。

public void Execute(object parameter)
{
    BackgroundWorker bgW = new BackgroundWorker();

    bgW.DoWork += (s, args) =>
    {
        AddTimeStampsToFiles(fileList, progressDialogViewModel);
        ResequenceFiles(fileList, progressDialogViewModel);
    };

    bgW.RunWorkerCompleted += (s, args) =>
    {
        m_ViewModel.RaiseImageProcessingEndingEvent();
    }; 

    m_ViewModel.RaiseImageProcessingStartingEvent();
    bgW.RunWorkerAsync();
}

为此,您可能需要将 fileListprogressDialogViewModel 值传递到 bgW.RunWorkerAsync() 方法。

对于多个值,我通常使用 Dictionary 对象,这样我就可以按名称引用值。

希望这有帮助。

According to MSDN

The Task.ContinueWith Method

Creates a continuation that executes asynchronously when the target
Task completes.

Which means that when the main task finishes, the other items in your two ContinueWith calls will run in parallel with one another.

To demonstrate this, you use the code below:

System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(() => Console.WriteLine("1"));
task.ContinueWith((t) => { System.Threading.Thread.Sleep(1000); Console.WriteLine("2"); });
task.ContinueWith((t) => Console.WriteLine("3"));

The Output Window will then read:

1
3
2

To help you out in your question, I have always used the System.ComponentModel.BackgroundWorker to run tasks serially. There are probably better ways, but this works for me for now.

public void Execute(object parameter)
{
    BackgroundWorker bgW = new BackgroundWorker();

    bgW.DoWork += (s, args) =>
    {
        AddTimeStampsToFiles(fileList, progressDialogViewModel);
        ResequenceFiles(fileList, progressDialogViewModel);
    };

    bgW.RunWorkerCompleted += (s, args) =>
    {
        m_ViewModel.RaiseImageProcessingEndingEvent();
    }; 

    m_ViewModel.RaiseImageProcessingStartingEvent();
    bgW.RunWorkerAsync();
}

For this to work, you may need to pass the fileList and progressDialogViewModel values in to the bgW.RunWorkerAsync() method.

For multiple values, I typically use a Dictionary<string, object> object so I can refer to the values by name.

Hope this helps.

苦妄 2024-12-09 23:35:02

找到了我的答案。在我的原始代码中,第二个和第三个任务都是第一个任务的延续。我更改了代码,为原始任务 (taskOne) 和延续任务(taskTwotaskThree)创建单独的任务。然后,taskTwo继续taskOnetaskThree继续taskTwo,就像这样:

// Background Task #1: Add time stamps to files
var taskOne = Task.Factory.StartNew(() => AddTimeStampsToFiles(fileList, progressDialogViewModel));

// Background Task #2: Resequence files
var taskTwo = taskOne.ContinueWith(t => this.ResequenceFiles(fileList, progressDialogViewModel));

// Announce that image processing is finished
var taskThree = taskTwo.ContinueWith(t => m_ViewModel.RaiseImageProcessingEndingEvent(), TaskScheduler.FromCurrentSynchronizationContext());

我已经接受了胖子的回答,因为它确实提供了一个可行的替代方案。然而,我在我的应用程序中使用了这个答案中的方法,因为我使用的是 TPL。

Found my answer. In my original code, both the second and third tasks were continuations of the first task. I changed my code to create separate tasks for the original task (taskOne) and the continuation tasks (taskTwo and taskThree). Then, taskTwo continues taskOne, and taskThree continues taskTwo, like this:

// Background Task #1: Add time stamps to files
var taskOne = Task.Factory.StartNew(() => AddTimeStampsToFiles(fileList, progressDialogViewModel));

// Background Task #2: Resequence files
var taskTwo = taskOne.ContinueWith(t => this.ResequenceFiles(fileList, progressDialogViewModel));

// Announce that image processing is finished
var taskThree = taskTwo.ContinueWith(t => m_ViewModel.RaiseImageProcessingEndingEvent(), TaskScheduler.FromCurrentSynchronizationContext());

I have accepted Fatty's answer, since it does present a viable alternative. However, I am using the approach in this answer in my application, since I am using TPL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文