2010年6月18日 星期五

TPL 學習日記(5): 等待一段時間

有時候,任務的執行需要等待一段時間後再繼續進行。
下面的程式,彷效進出貨。進貨及出貨各需2.5秒,如果使用者按 Stop 鍵,程式就會「停止」。

Canecl2.xaml
<Window x:Class="TPL_CancelAnApp.Cancel2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Cancel2" Height="350" Width="525">
    <Grid>
    <StackPanel >
      <TextBlock x:Name="txtTotal" />
      <Button x:Name="btnStart" Click="btnStart_Click">Start</Button>
      <Button x:Name="btnStop" Click="btnStop_Click" Content="Stop"></Button>
      <TextBlock Name="txtStatus" />
    </StackPanel>
  </Grid>
</Window>

 

Cancel2.xaml.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace TPL_CancelAnApp
{
  public partial class Cancel2 : Window
  {
    //建立 CancellationTokenSource
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    Task task;
    public Cancel2()
    {
      InitializeComponent();
      InitialTask();
    }

    private void InitialTask()
    {
      CancellationToken token = tokenSource.Token;
      task = new Task(() =>
      {
        long total = 0;
        while (total < int.MaxValue)
        {
          total++;
          Action<string> action = new Action<string>(s => txtTotal.Text = s);
          txtTotal.Dispatcher.BeginInvoke(action, DispatcherPriority.Normal, total.ToString() + " 進貨");
          Thread.Sleep(2500);
          txtTotal.Dispatcher.BeginInvoke(action, DispatcherPriority.Normal, total.ToString() + " 出貨");
          Thread.Sleep(2500);
          token.ThrowIfCancellationRequested();
        };
      }, token);
    }

    private void btnStart_Click(object sender, RoutedEventArgs e)
    {
      txtStatus.Text = "進行中";
      task.Start();
      btnStart.IsEnabled = false;
    }

    private void btnStop_Click(object sender, RoutedEventArgs e)
    {
      txtStatus.Text = "使用者要求停止";
      tokenSource.Cancel();
    }
  }
}

上面的程式是可以執行的。但有個問題,當使用者按 Stop 鍵,程式並不會立即停止,而是需要等到進貨及出貨完成(各2.5秒)後, 才到token.ThrowIfCancellationRequested這一行決定是否取消。因此,一定會出貨完。 有沒有方法可以立即停止呢?

更新

問題出在 Thread.Sleep(2500),是無論如何都會「睡」個 2.5 秒,與目前的 Task 無關。

此時,我們需要使用 CancellationToken.WaitHandle.WaitOne(2500) 來模擬了。程式會等待取消 2.5 秒,並回傳是否有被取消。

程式如下

private void InitialTask()
{
  CancellationToken token = tokenSource.Token;
  task = new Task(() =>
  {
    long total = 0;
    while (total < int.MaxValue)
    {
      total++;
      Action<string> action = new Action<string>(s => txtTotal.Text = s);
      txtTotal.Dispatcher.BeginInvoke(action, DispatcherPriority.Normal, total.ToString() + " 進貨");
      //Thread.Sleep(2500);
      bool isCanceled = false;
      isCanceled = token.WaitHandle.WaitOne(2500);
      if (isCanceled)
        token.ThrowIfCancellationRequested();
      txtTotal.Dispatcher.BeginInvoke(action, DispatcherPriority.Normal, total.ToString() + " 出貨");
      //Thread.Sleep(2500);
      isCanceled = token.WaitHandle.WaitOne(2500);
      if (isCanceled)
      token.ThrowIfCancellationRequested();
    };
  }, token);
}
這樣修改後,按了Stop 鍵就會立即停止了。 範例程式下載

沒有留言:

Share with Facebook