首页 > 代码库 > 异步更新界面方法

异步更新界面方法

代码中频繁出现下面描述的模式,

某些操作调用服务端接口获取数据、执行复杂的数据解析,这些操作不能阻塞UI线程,必须异步执行,但通常这些操作执行完后需要对界面元素进行更新。

就是这么个模式在代码中频繁出现。

之前代码解决此问题的方式是使用thread,一般流程是下面这样(工作任务一般在模型中有封装,这些操作是阻塞的),

   1.  首先我们必须把这些接口封装成thread要求的接口,通常使用内部类(之前我们用的是VS05版本,没有lambda)

   2.  调用boost::thread

   3.  执行完后PostMessage到某个窗体

   4. 修改该窗体代码,加上消息捕获和消息处理方法

   5. 等(取决想要什么额外操作和程序员的写法)


使用过类似方法的都知道有多痛苦,想写的心都没了,有时因此还会偷懒,比如在异步中更新界面元素。

通常上面说的每一步都是很短小且用一次的代码,但使用上面的方式必须把小短的代码分散到不同的源文件中,增加了对象间的引用交互,最主要的是麻烦,每次需要执行该模式时,执行上面的每一步都是痛苦的。

想要的其实很简单:异步执行指定的动作,等执行完后记得更新界面元素。

既然是频繁出现的模式,那就把该模式封装起来,接口中只需填写

   1. 需要执行什么动作

   2. 如何更新界面元素

参照C#中的then,我们最终要的大概是如下写法,

aync([=]()->DWORD_PTR {

    std::string result;
    m_impl->user->getCrashResult(begin->first, result);
    return CrashResult::GetResultCount(result);

}).then([=](DWORD_PTR result) {

    UpdateCrashResult(unsigned int)begin->first.fileId(), (int)result);

});

aync中的代码在工作线程中执行,then中的代码在UI线程中执行(现在VS版本是VS10,有lambda支持)。

实现想法如下,

aync内部会把lambda以及then中的行为封装为对象,存储到一个数组中,aync要求的接口是返回DWORD_PTR类型的无参函数,返回的值在then中作为参数,DWORD_PTR就是一个int型,说是指针也好,这东西在Windows平台传参很常见,个人很喜欢这个,因为用法很灵活。

aync中会把lambda转换成无参无返回值类型,以适应thread接口,之后放入线程池中,

BimWorks::GetAsyncAction().Append([&]() {

    this->result_ = func_();
    GetBWAsyncInvoke().Submit(*this);

});

在func_执行完后调用  GetBWAsyncInvoke().Submit(*this); 该方法执行post消息到主线程动作,这里使用一个隐藏的static窗体作为消息的接收方。

消息处理函数简单的从队列中找到该lambda对应的对象,执行保存的then动作。执行完后从队列中移除该对象。

完成。

这个实现只是一个初步的方法,有些细节需要完善的。

这种方式只适用一种固定的模式,即最开始描述的模式,thread太底层了,我们往往需要更高层的模式,比如task这样的概念,VS10中的PPL库提供了其它一些模式,比如task,并行算法、容器,流水线,协作等,感觉都不是我们想要的这种非常轻量的写法。