尝试创建基于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. 升级Xcode 10 后报错问题记录([CP] Copy Pods Resources)

    1.升级Xcode到Version 10.0 (10A255)后,运行已有项目,报如下错误: error: Multiple commands produce '/Users/galahad/Libr ...

  2. 第十二周作业_PSP总结报告

    回顾1 (1)回想一下你曾经对计算机专业的畅想 当初你是如何做出选择计算机专业的决定的?经过一个学期,你的看法改变了么,为什么? 你认为过去接触到的课程是否符合你对计算机专业的期待,为什么?经过一个学 ...

  3. 修复webpack自动刷新页面慢的问题

    新建.babelrc文件,配置如下 { "presets": [ "es2015" ], "ignore":[ "react-ro ...

  4. 面向对象程序设计第三次作业-Calculator

    题目: 最终代码: Scan.h: Print.h: Calaulator.cpp: 解题过程 看到题目后,在查询之后明白了这是多文件的题目,然后通过翁凯老师的视频讲解知道了.h和.cpp文件的区别和 ...

  5. Week-2-作业1

    第一章 概论 1.什么是程序? 答:在学习软件工程导论前,我们已经学习了一些计算机语言和数据结构这样的课程,并深刻的知道“程序=数据结构+算法”,但在学习中还是会产生如书中1.1讲所提到的那些疑问,二 ...

  6. Gradle入门(5):创建二进制发布版本

    在创建了一个实用的应用程序之后,我们可能想将其与他人分享.其中一种方式就是创建一个可以从网站上下载的二进制文件. 这篇教程描述了如何创建一个二进制发布版本,满足以下需求: 二进制发布一定不能使用所谓的 ...

  7. sql中exists和not exists的用法

    该文转载自:http://www.cnblogs.com/mytechblog/articles/2105785.html sql中exists,not exists的用法 exists : 强调的是 ...

  8. Scrum Meeting Beta - 8

    Scrum Meeting Beta - 8 NewTeam 2017/12/7 地点:新主楼F座二楼 任务反馈 团队成员 完成任务 计划任务 安万贺 完成了博文详情的存储Issue #150Pull ...

  9. PHP 配置默认SSL CA证书

    1.从CURL 官网下载CA 证书(当然也可以选择自己创建SSL CA证书,详情参考 https://blog.csdn.net/scuyxi/article/details/54898870 ,或自 ...

  10. Cocos2d入门及第一次运行时遇到的问题

    先通过github下载cocos2d.これ:https://github.com/ZhouWeikuan/cocos2d 进入上面的网址后,如果不会用git或者svn的朋友就在页面的右下角点那个“Do ...