.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

时间: 2024-11-09 21:41:20

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

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

手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                              ----- By PiggyXP(小猪) 前 言 本系列里完毕port的代码在两年前就已经写好了,可是因为许久没有写东西了,不知该怎样提笔,所以这篇文档总是在酝酿之中--酝酿了两年之后,最终决定開始动笔了,但愿还不算晚-.. 这篇文档我很具体而且图文并茂的介绍了关于网络编程模型中完毕

【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监测EV3端口数据

在前两篇文章中,我们对iOS与EV3混合机器人编程做了一个基本的设想,并且介绍了要完成项目所需的软硬件准备和知识准备. 那么在今天这一篇文章中,我们将直接真正开始项目实践. ==第一个项目: EV3 Port Viewer== 项目目的:在iOS设备上通过WiFi连接EV3并且读取EV3每个端口的数据. 大家可以一周之后在App Store上搜索EV3 Port Viewer,那么我已经做了一个范例App发布了,正在审核中 应用的基本使用要求:将EV3和iPhone同时连接到同一个WiFi网络中

TCP/IP网络编程系列之三

TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值,而是为了区分程序中创建的套接字而分配给套接字的序号. 网络地址 网络地址分为IPV4和IPV6,分别你别为4个字节地址簇和6个字节地址簇.IPV4标准的4个字节的地址分为网络地址和主机地址,且分为A.B.C.D.E 等类型.一般很少用到E类型.如下图所示:net-id指网络ID,host-id指主机

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

现在普通PC平台上面多核处理器的普及,让我们领教了能够利用多核进行并行计算的软件的处理能力,同时继承更多地核心正是当前处理器发展的趋势. 但是作为一个.NET开发人员,是否有时候会发现你的程序占用了其中一个核心的大部分运行时间,甚至达到了100%,除了继续优化处理问题的算法. 那么还有方法能够利用CPU的其它核心为你的应用程序所用么?答案是显然可以的. 你所要做的,就是把应用程序的任务分割成块,使各块在同一时间运行. .Net 4.0引入了一种新的编程模型,结合VS2010使得开发并行应用程序变

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

TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值,而是为了区分程序中创建的套接字而分配给套接字的序号. 网络地址 网络地址分为IPV4和IPV6,分别你别为4个字节地址簇和6个字节地址簇.IPV4标准的4个字节的地址分为网络地址和主机地址,且分为A.B.C.D.E 等类型.一般很少用到E类型.如下图所示:net-id指网络ID,host-id指主机

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

8天玩转并行开发 8天玩转并行开发——第一天 Parallel的使用 8天玩转并行开发——第二天 Task的使用 8天玩转并行开发——第三天 plinq的使用 8天玩转并行开发——第四天 同步机制(上) 8天玩转并行开发——第五天 同步机制(下) 8天玩转并行开发——第六天 异步编程模型 8天玩转并行开发——第七天 简要分析任务与线程池 8天玩转并行开发——第八天 用VS性能向导解剖你的程序 并行编程系列 .NET 4 并行(多核)编程系列之一入门介绍 .NET 4 并行(多核)编程系列之二 从

.net中的并行编程系列-1.基础知识

最近半年一直研究用.net进行并行程序的开发与设计,再研究的过程中颇有收获,所以画了一个图总结了一下并行编程的基础知识点,这些知识点是并行编程的基础,有助于我们编程高性能的程序,里面的某些结构实现机制也蕴含着丰富的软件设计思想,在后续的文章中我会对图里面提到某些数据结构或同步机制的源码进行分析. 注:虽然使用的平台是.net ,但大部分知识点和平台以及语言无关,相关数据结构其他相关平台都有实现,包括优化手段都非常相似. .net中的并行编程系列-1.基础知识,布布扣,bubuko.com

.NET 4 并行(多核)编程系列之一入门介绍

本系列文章将会对.NET 4中的并行编程技术(也称之为多核编程技术)以及应用作全面的介绍. 本篇文章的议题如下:  1. 并行编程和多线程编程的区别.  2. 并行编程技术的利弊  3. 何时采用并行编程 系列文章链接: .NET 4 并行(多核)编程系列之一入门介绍 .NET 4 并行(多核)编程系列之二 从Task开始 .NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之四 Task的休眠 .NET 并行(多核)编程系列之五 Task执行和异常处理

并行编程入门

目录 1. 并行编程简介 2. MapReduce 2.1 MapReduce简介 2.2 MapReduce框架 2.3 Hadoop介绍 2.4 Hadoop基本类 2.5 Hadoop编程实例 1.并行编程简介 1.1.并行编程作用,用途 商业用途,科学计算,大数据分析 1.2.并行编程兴起原因 目前的串行编程的局限性 使用的流水线等隐式并行模式的局限性 硬件的发展 1.3.并行算法设计原则步骤 a.分析问题 b.分解问题 其中分解方法有: 数据分解 递归分解 探测性分解 推测性分解 混合