尝试创建基于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 = ;
if (longRunningTaskWithTimeout.Wait(miliSec))
{
synchronizationContext.Post(s =>
{
Worker1StatusCollection.Add(string.Format("Task completed with allotted time:{0}s" + miliSec / ));
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 / ));
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 = ;
if (longRunningTaskWithTimeout.Wait(miliSec))
{
synchronizationContext.Post(s =>
{
Worker2StatusCollection.Add(string.Format("Task completed with allotted time:{0}s", miliSec / ));
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 / ));
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 = ;
if (longRunningTaskWithTimeout.Wait(miliSec))
{
synchronizationContext.Post(s =>
{
Worker3StatusCollection.Add(string.Format("Task completed with allotted time:{0}s", miliSec / ));
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 / ));
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());
return "Data from DataWithLongTime";
}
public string GetDataWithShortTime()
{
return "Data from DataWithShortTime";
}
}

基本的思想就是这样。

作者:Andy Zeng

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

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

.Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果的更多相关文章

  1. .NET 4 并行(多核)编程系列之三 从Task的取消

    原文:.NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之三 从Task的取消 前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程 ...

  2. 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三

       手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                    ...

  3. .NET 4.0 任务和并行编程系列

    8天玩转并行开发 8天玩转并行开发——第一天 Parallel的使用 8天玩转并行开发——第二天 Task的使用 8天玩转并行开发——第三天 plinq的使用 8天玩转并行开发——第四天 同步机制(上 ...

  4. TCP/IP网络编程系列之三(初级)

    TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...

  5. C#并行编程系列-文章导航

    菜鸟初步学习,不对的地方请大神指教,参考<C#并行编程高级教程.pdf> 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C# ...

  6. (转载)完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三

    转自:http://blog.csdn.net/piggyxp/article/details/6922277 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何 ...

  7. .Net并行编程系列之一:并行基础

    现在普通PC平台上面多核处理器的普及,让我们领教了能够利用多核进行并行计算的软件的处理能力,同时继承更多地核心正是当前处理器发展的趋势. 但是作为一个.NET开发人员,是否有时候会发现你的程序占用了其 ...

  8. Java并发编程系列之三十二:丢失的信号

    这里的丢失的信号是指线程必须等待一个已经为真的条件,在開始等待之前没有检查等待条件.这样的场景事实上挺好理解,假设一边烧水,一边看电视,那么在水烧开的时候.由于太投入而没有注意到水被烧开. 丢失的信号 ...

  9. 【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监測EV3port数据

    在前两篇文章中,我们对iOS与EV3混合机器人编程做了一个主要的设想.而且介绍了要完毕项目所需的软硬件准备和知识准备. 那么在今天这一篇文章中,我们将直接真正開始项目实践. ==第一个项目: EV3 ...

随机推荐

  1. 暑假作业app博客

    一.光照传感器 界面 简介 运用了传感器类,通过手机的感应区根据当时的光照强度显示出数据. 核心代码 protected void onCreate(Bundle savedInstanceState ...

  2. 《Spring1之第五次站立会议》

    <第五次站立会议> 昨天:试着做了一下主框架里的在线人数的显示代码: 今天:向小伙伴们请教了我代码的错误的解决方法以及对TCP/IP协议进行了相关的了解. 遇到的问题:虽然基本原理不难理解 ...

  3. Internet History, Technology and Security (Week5.1)

    Week5 The Transport layer is built on the Internetwork layer and is what makes our network connectio ...

  4. Internet History, Technology and Security (Week4)

    Week4. We are now moving into Week 4! This week, we will be covering commercialization and growth. T ...

  5. 如何在Sql Server 2000中用一条语句实现树的查询

    以公司的网站栏目表为例,表结构(表名:Subject)如下:   字段名称 字段类型 字段长度 id int 4 NAME varchar 200 PARENTID int 4 CODE varcha ...

  6. Go 自学笔记

    1. 最近花时间简单自学了一下go语言的语法..为了保证自己不是每次从0 开始 这次简单进行一下记录 保证 学习 效果. 2. 安装 直接下载go的包 进行安装 以及 暗转goland2018.3 进 ...

  7. 数组 javaScript权威指南笔记

    创建数组 var a=[1,2,3,4] var arr=new Array() var arr=new Array(10);//创建长度为10的数组   var arr=new Array(1,2, ...

  8. 基于JQuery的前端form表单操作

    Jquery的前端表单操作:     jquery提供了良好的方法封装,在一些基本的操作的时候,能节省很多的麻烦,其中,在具体使用时,form表单的数据提交是最频繁也最常见的前后数据交换方式,所以在前 ...

  9. Window环境下搭建Vue.js开发环境

    原文链接:http://blog.csdn.net/solo95/article/details/72084907 笔者最近在进行前端的学习,在点完了HTML5.CSS3.JavaScript等技能树 ...

  10. unix常用命令记录

    1. ls 命令:列出文件及文件夹 ls -a 列出目录下的所有文件,包括以 . 开头的隐含文件.ls -b 把文件名中不可输出的字符用反斜杠加字符编号(就象在C语言里一样)的形式列出.ls -c 输 ...