zoukankan      html  css  js  c++  java
  • .Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果

    尝试创建基于MVVM三层架构的异步任务:

    场景:View层触发ViewModel层的动作请求,ViewModel层异步的从Model层查询数据,当数据返回或者请求超时时正确更新ViewModel层数据并触发View层的UI更新。

    要求:View层保持UI响应,ViewModel层实现有超时控制的异步调用并返回结果

    ---------------------------

    实现三个Worker,Worker1演示调用超时返回,Worker2演示无超时成功返回,Worker3演示同样演示无超时返回

    设计WPF窗口如下:

    <Window x:Class="TimeOutTask.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:TimeOutTask"
            Title="TaskTimeOutDemo" Height="480" Width="650">
        <Window.DataContext>
            <local:ViewModel></local:ViewModel>
        </Window.DataContext>
        <Window.Resources>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Height" Value="30"></Setter>
                <Setter Property="Margin" Value="5"></Setter>
                <Setter Property="Width" Value="80"></Setter>
            </Style>
            <Style TargetType="{x:Type ListBox}">
                <Setter Property="Height" Value="120"></Setter>
                <Setter Property="Margin" Value="5"></Setter>
                <Setter Property="Width" Value="500"></Setter>
            </Style>
        </Window.Resources>
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button Command="{Binding Worker1Command}" Content="Start Worker1"  />
                <ListBox  Background="LightGray" ItemsSource="{Binding Worker1StatusCollection}"  />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button  Command="{Binding Worker2Command}" Content="Start Worker2"  />
                <ListBox  Background="LightGray" ItemsSource="{Binding Worker2StatusCollection}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button  Command="{Binding Worker3Command}" Content="Start Worker3"  />
                <ListBox Background="LightGray" ItemsSource="{Binding Worker3StatusCollection}"/>
            </StackPanel>
        </StackPanel>
    </Window>

    ViewModel类设计如下:

    其中 synchronizationContext 为UI线程的同步上下文,作为Task返回结果是的回调更新ViewModel层数据之用,注意:View层和ViewModel是同时运行在主线程之上的。

    注意我们创建的实际工作task的方法是Task(Action action, TaskCreationOptions creationOptions);

    其中TaskCreationOptions 参数选择的是 TaskCreationOptions.LongRunning,此参数保证创建的task始终运行在同一个线程之上,减少线程间切换上下文的损失。

    另外,传入timeout的方式是Task的方法public bool Wait(int millisecondsTimeout);返回True时表示在规定时间内返回了结果,放回false表示未在指定时间内返回结果。

        public class ViewModel : INotifyPropertyChanged
        {
            Model engineSimulator;
            private SynchronizationContext synchronizationContext;
            public ViewModel()
            {
                engineSimulator = new Model();
                synchronizationContext = System.Threading.SynchronizationContext.Current;
                worker1StatusCollection = new ObservableCollection<string>();
                Worker1Command = new DelegateCommand(Worker1, () => !IsWorker1Busy);
    
                Worker2StatusCollection = new ObservableCollection<string>();
                Worker2Command = new DelegateCommand(Worker2, () => !IsWorker2Busy);
    
                Worker3StatusCollection = new ObservableCollection<string>();
                Worker3Command = new DelegateCommand(Worker3, () => !IsWorker3Busy);
            }
    
            #region Worker1
            private ObservableCollection<string> worker1StatusCollection;
            public ObservableCollection<string> Worker1StatusCollection
            {
                get { return worker1StatusCollection; }
                set { PropertyChanged.ChangeAndNotify(ref worker1StatusCollection, value, () => Worker1StatusCollection); }
            }
            public void Worker1()
            {
                Worker1StatusCollection.Add(string.Format("Start Worker1 at:{0}", DateTime.Now.ToLongTimeString()));
                IsWorker1Busy = true;
                ((DelegateCommand)Worker1Command).RaiseCanExecuteChanged();
                Task.Factory.StartNew((Action)(() =>
                {
                    var longRunningTaskWithTimeout = new Task<string>(() =>
                    {
                        return engineSimulator.GetDataWithLongTime();
                    }, TaskCreationOptions.LongRunning);
    
                    longRunningTaskWithTimeout.Start();
                    int miliSec = 5000;
                    if (longRunningTaskWithTimeout.Wait(miliSec))
                    {
                        synchronizationContext.Post(s =>
                        {
                            Worker1StatusCollection.Add(string.Format("Task completed with allotted time:{0}s" + miliSec / 1000));
                            Worker1StatusCollection.Add("Task Status:" + longRunningTaskWithTimeout.Status.ToString());
                            Worker1StatusCollection.Add("ResultFromEngine:"+longRunningTaskWithTimeout.Result);
                            Worker1StatusCollection.Add(string.Format("End Worker1 at:{0}", DateTime.Now.ToLongTimeString()));
                            IsWorker1Busy = false;
                            ((DelegateCommand)Worker1Command).RaiseCanExecuteChanged();
                        }, null);
                    }
                    else
                    {
                        synchronizationContext.Post(s =>
                        {
                            Worker1StatusCollection.Add(string.Format("Task Not completed with allotted time:{0}s", miliSec / 1000));
                            Worker1StatusCollection.Add("Status:" + longRunningTaskWithTimeout.Status.ToString());
                            Worker1StatusCollection.Add(string.Format("End Worker1 at:{0}", DateTime.Now.ToLongTimeString()));
                            IsWorker1Busy = false;
                            ((DelegateCommand)Worker1Command).RaiseCanExecuteChanged();
                        }, null);
                    }
                }));
            }
            private bool isWorker1Busy;
            public bool IsWorker1Busy
            {
                get { return isWorker1Busy; }
                set { PropertyChanged.ChangeAndNotify(ref isWorker1Busy, value, () => IsWorker1Busy); }
            }
            public ICommand Worker1Command { get; private set; }
            #endregion
    
            #region Worker2
            private ObservableCollection<string> worker2StatusCollection;
            public ObservableCollection<string> Worker2StatusCollection
            {
                get { return worker2StatusCollection; }
                set { PropertyChanged.ChangeAndNotify(ref worker2StatusCollection, value, () => Worker2StatusCollection); }
            }
            public void Worker2()
            {
                Worker2StatusCollection.Add(string.Format("Start Worker2 at:{0}", DateTime.Now.ToLongTimeString()));
                IsWorker2Busy = true;
                ((DelegateCommand)Worker2Command).RaiseCanExecuteChanged();
                Task.Factory.StartNew((Action)(() =>
                {
                    var longRunningTaskWithTimeout = new Task<string>(() =>
                    {
                        return engineSimulator.GetDataWithLongTime();
                    }, TaskCreationOptions.LongRunning);
    
                    longRunningTaskWithTimeout.Start();
                    int miliSec = 11000;
                    if (longRunningTaskWithTimeout.Wait(miliSec))
                    {
                        synchronizationContext.Post(s =>
                        {
                            Worker2StatusCollection.Add(string.Format("Task completed with allotted time:{0}s", miliSec / 1000));
                            Worker2StatusCollection.Add("Tast Status:" + longRunningTaskWithTimeout.Status.ToString());
                            Worker2StatusCollection.Add("ResultFromEngine:" + longRunningTaskWithTimeout.Result);
                            Worker2StatusCollection.Add(string.Format("End Worker2 at:{0}", DateTime.Now.ToLongTimeString()));
                            IsWorker2Busy = false;
                            ((DelegateCommand)Worker2Command).RaiseCanExecuteChanged();
                        }, null);
                    }
                    else
                    {
                        synchronizationContext.Post(s =>
                        {
                            Worker2StatusCollection.Add(string.Format("Task Not completed with allotted time:{0}s", miliSec / 1000));
                            Worker2StatusCollection.Add("Task Status:" + longRunningTaskWithTimeout.Status.ToString());
                            Worker2StatusCollection.Add(string.Format("End Worker2 at:{0}", DateTime.Now.ToLongTimeString()));
                            IsWorker2Busy = false;
                            ((DelegateCommand)Worker2Command).RaiseCanExecuteChanged();
                        }, null);
                    }
                }));
            }
            private bool isWorker2Busy;
            public bool IsWorker2Busy
            {
                get { return isWorker2Busy; }
                set { PropertyChanged.ChangeAndNotify(ref isWorker2Busy, value, () => IsWorker2Busy); }
            }
            public ICommand Worker2Command { get; private set; }
            #endregion
    
            #region Worker3
            private ObservableCollection<string> worker3StatusCollection;
            public ObservableCollection<string> Worker3StatusCollection
            {
                get { return worker3StatusCollection; }
                set { PropertyChanged.ChangeAndNotify(ref worker3StatusCollection, value, () => Worker3StatusCollection); }
            }
            public void Worker3()
            {
                Worker3StatusCollection.Add(string.Format("Start Worker3 at:{0}", DateTime.Now.ToLongTimeString()));
                IsWorker3Busy = true;
                ((DelegateCommand)Worker3Command).RaiseCanExecuteChanged();
                Task.Factory.StartNew((Action)(() =>
                {
                    var longRunningTaskWithTimeout = new Task<string>(() =>
                    {
                        return engineSimulator.GetDataWithShortTime();
                    }, TaskCreationOptions.LongRunning);
    
                    longRunningTaskWithTimeout.Start();
                    int miliSec = 1000;
                    if (longRunningTaskWithTimeout.Wait(miliSec))
                    {
                        synchronizationContext.Post(s =>
                        {
                            Worker3StatusCollection.Add(string.Format("Task completed with allotted time:{0}s", miliSec / 1000));
                            Worker3StatusCollection.Add("Task Status:" + longRunningTaskWithTimeout.Status.ToString());
                            Worker3StatusCollection.Add("ResultFromEngine:" + longRunningTaskWithTimeout.Result);
                            Worker3StatusCollection.Add(string.Format("End Worker3 at:{0}", DateTime.Now.ToLongTimeString()));
                            IsWorker3Busy = false;
                            ((DelegateCommand)Worker3Command).RaiseCanExecuteChanged();
                        }, null);
                    }
                    else
                    {
                        synchronizationContext.Post(s =>
                        {
                            Worker3StatusCollection.Add(string.Format("Task Not completed with allotted time:{0}", miliSec / 1000));
                            Worker3StatusCollection.Add("Status:" + longRunningTaskWithTimeout.Status.ToString());
                            Worker3StatusCollection.Add(string.Format("End Worker3 at:{0}", DateTime.Now.ToLongTimeString()));
                            IsWorker3Busy = false;
                            ((DelegateCommand)Worker3Command).RaiseCanExecuteChanged();
                        }, null);
                    }
                }));
            }
            private bool isWorker3Busy;
            public bool IsWorker3Busy
            {
                get { return isWorker3Busy; }
                set { PropertyChanged.ChangeAndNotify(ref isWorker3Busy, value, () => IsWorker3Busy); }
            }
            public ICommand Worker3Command { get; private set; }
            #endregion
    
            public event PropertyChangedEventHandler PropertyChanged;
        }

    模仿Model层的类定义如下:一个方法等待10s模仿底层操作(Worker1,Worker2使用),另一个方法直接返回(供Worker3使用)

     public class Model
        {
            public string GetDataWithLongTime()
            {
                Thread.Sleep(TimeSpan.FromSeconds(10));
                return "Data from DataWithLongTime";
            }
            public string GetDataWithShortTime()
            {
                return "Data from DataWithShortTime";
            }
        }

    基本的思想就是这样。

    作者:Andy Zeng

    欢迎任何形式的转载,但请务必注明出处。

    http://www.cnblogs.com/andyzeng/p/3732156.html

  • 相关阅读:
    个人应该做什么样的网站?
    得对xls文件作操作
    正则替换
    31 反馈的判断方法(2)
    AD 层次原理图设计 自上而下
    27 多级放大电路的频率响应与第四章集成运放的电流源电路
    P28 集成运放的电流源电路(2)
    32 负反馈放大电路的方块图
    ESP8266固件AT指令连接EMQ 的MQTT服务器
    33 深度负反馈的实质与放大倍数分析
  • 原文地址:https://www.cnblogs.com/andyzeng/p/3732156.html
Copyright © 2011-2022 走看看