当前位置: 首页 > 编程日记 > 正文

Await, and UI, and deadlocks! Oh my!

It’s been awesome seeing the level of interest developers have had for the Async CTP and how much usage it’s getting.  Of course, with any new technology there are bound to be some hiccups.  One issue I’ve seen arise now multiple times is developers accidentally deadlocking their application by blocking their UI thread, so I thought it would be worthwhile to take a few moments to explore the common cause of this and how to avoid such predicaments.

At its core, the new async language functionality aims to restore the ability for developers to write the sequential, imperative code they’re used to writing, but to have it be asynchronous in nature rather than synchronous.  That means that when operations would otherwise tie up the current thread of execution, they’re instead offloaded elsewhere, allowing the current thread to make forward progress and do other useful work while, in effect, asynchronously waiting for the spawned operation to complete.  In both server and client applications, this can be crucial for application scalability, and in client applications in particular it’s also really useful for responsiveness.

Most UI frameworks, such as Windows Forms and WPF, utilize a message loop to receive and process incoming messages.  These messages include things like notifications of keys being typed on a keyboard, or buttons being clicked on a mouse, or controls in the user interface being manipulated, or the need to refresh an area of the window, or even the application sending itself a message dictating some code to be executed.  In response to these messages, the UI performs some action, such as redrawing its surface, or changing the text being displayed, or adding items to one of its controls., or running the code that was posted to it.  The “message loop” is typically literally a loop in code, where a thread continually waits for the next message to arrive, processes it, goes back to get the next message, processes it, and so on.  As long as that thread is able to quickly process messages as soon as they arrive, the application remains responsive, and the application’s users remain happy.  If, however, processing a particular message takes too long, the thread running the message loop code will be unable to pick up the next message in a timely fashion, and responsiveness will decrease.  This could take the form of pauses in responding to user input, and if the thread’s delays get bad enough (e.g. an infinite delay), the application “hanging”.

In a framework like Windows Forms or WPF, when a user clicks a button, that typically ends up sending a message to the message loop, which translates the message into a call to a handler of some kind, such as a method on the class representing the user interface, e.g.:

private void button1_Click(object sender, RoutedEventArgs e)       {            string s = LoadString();            textBox1.Text = s;        }

Here, when I click the button1 control, the message will inform WPF to invoke the button1_Click method, which will in turn run a method LoadString to get a string value, and store that string value into the textBox1 control’s Text property.  As long as LoadString is quick to execute, all is well, but the longer LoadString takes, the more time the UI thread is delayed inside button1_Click, unable to return to the message loop to pick up and process the next message.

To address that, we can choose to load the string asynchronously, meaning that rather than blocking the thread calling button1_Click from returning to the message loop until the string loading has completed, we’ll instead just have that thread launch the loading operation and then go back to the message loop.  Only when the loading operation completes will we then send another message to the message loop to say “hey, that loading operation you previously started is done, and you can pick up where you left off and continue executing.”  Imagine we had a method:

public Task<string> LoadStringAsync();

This method will return very quickly to its caller, handing back a .NET Task<string> object that represents the future completion of the asynchronous operation and its future result.  At some point in the future when the operation completes, the task object will be able to hand out the operations’ result, which could be the string in the case of successful loading, or an exception in the case of failure.  Either way, the task object provides several mechanisms to notify the holder of the object that the loading operation has completed.  One way is to synchronously block waiting for the task to complete, and that can be accomplished by calling the task’s Wait method, or by accessing its Result, which will implicitly wait until the operation has completed… in both of these cases, a call to these members will not complete until the operation has completed.  An alternative way is to receive an asynchronous callback, where you register with the task a delegate that will be invoked when the task completes.  That can be accomplished using one of the Task’s ContinueWith methods.  With ContinueWith, we can now rewrite our previous button1_Click method to not block the UI thread while we’re asynchronously waiting for the loading operation to complete:

private void button1_Click(object sender, RoutedEventArgs e)       {            Task<string> s = LoadStringAsync();            s.ContinueWith(delegate { textBox1.Text = s.Result; }); // warning: buggy        }

This does in fact asynchronously launch the loading operation, and then asynchronously run the code to store the result into the UI when the operation completes.  However, we now have a new problem.  UI frameworks like Windows Forms, WPF, and Silverlight all place a restriction on which threads are able to access UI controls, namely that the control can only be accessed from the thread that created it.  Here, however, we’re running the callback to update the Text of textBox1on some arbitrary thread, wherever the Task Parallel Library (TPL) implementation of ContinueWith happened to put it.  To address this, we need some way to get back to the UI thread.  Different UI frameworks provide different mechanisms for doing this, but in .NET they all take basically the same shape, a BeginInvoke method you can use to pass some code as a message to the UI thread to be processed:

private void button1_Click(object sender, RoutedEventArgs e)       {            Task<string> s = LoadStringAsync();            s.ContinueWith(delegate            {                Dispatcher.BeginInvoke(new Action(delegate                {                    textBox1.Text = s.Result;                }));            });        }

The .NET Framework further abstracts over these mechanisms for getting back to the UI thread, and in general a mechanism for posting some code to a particular context, through the SynchronizationContext class.  A framework can establish a current context, available through the SynchronizationContext.Current property, which provides a SynchronizationContext instance representing the current environment.  This instance’s Post method will marshal a delegate back to this environment to be invoked: in a WPF app, that means bringing you back to the dispatcher, or UI thread, you were previously on.  So, we can rewrite the previous code as follows:

private void button1_Click(object sender, RoutedEventArgs e)       {            var sc = SynchronizationContext.Current;            Task<string> s = LoadStringAsync();            s.ContinueWith(delegate            {                sc.Post(delegate { textBox1.Text = s.Result; }, null);            });        }

and in fact this pattern is so common, TPL in .NET 4 provides the TaskScheduler.FromCurrentSynchronizationContext() method, which allows you to do the same thing with code like:

private void button1_Click(object sender, RoutedEventArgs e)       {            LoadStringAsync().ContinueWith(s => textBox1.Text = s.Result,                 TaskScheduler.FromCurrentSynchronizationContext());        }

As mentioned, this works by “posting” the delegate back to the UI thread to be executed.  That posting is a message like any other, and it requires the UI thread to go through its message loop, pick up the message, and process it (which will result in invoking the posted delegate).  In order for the delegate to be invoked then, the thread first needs to return to the message loop, which means it must leave the button1_Click method.

Now, there’s still a fair amount of boilerplate code to write above, and it gets orders of magnitude worse when you start introducing more complicated flow control constructs, like conditionals and loops.  To address this, the new async language feature allows you to write this same code as:

private void button1_Click(object sender, RoutedEventArgs e)       {            string s = await LoadStringAsync();            textBox1.Text = s;        }

For all intents and purposes, this is the same as the previous code shown, and you can see how much cleaner it is… in fact, it’s close to identical  in the code required to our original synchronous implementation.  But, of course, this one is asynchronous: after calling LoadStringAsync and getting back the Task<string> object, the remainder of the function is hooked up as a callback that will be posted to the current SynchronizationContext in order to continue execution on the right thread when the loading is complete.  The compiler is layering on some really helpful syntactic sugar here.

Now things get interesting. Let’s imagine LoadStringAsync is implemented as follows:

static async Task<string> LoadStringAsync()       {            string firstName = await GetFirstNameAsync();            string lastName = await GetLastNameAsync();            return firstName + " " + lastName;        }

LoadStringAsync is implemented to first asynchronously retrieve a first name, then asynchronously retrieve a last name, and then return the concatenation of the two.  Notice that it’s using “await”, which, as pointed out previously, is similar to the aforementioned TPL code that uses a continuation to post back to the synchronization context that was current when the await was issued.  So, here’s the crucial point: for LoadStringAsync to complete (i.e. for it to have loaded all of its data and returned its concatenated string, completing the task it returned with that concatenated result), the delegates it posted to the UI thread must have completed.  If the UI thread is unable to get back to the message loop to process messages, it will be unable to pick up the posted delegates that resulted from the asynchronous operations in LoadStringAsync completing, which means the remainder of LoadStringAsync will not run, which means the Task<string> returned from LoadStringAsync will not complete.  It won’t complete until the relevant messages are processed by the message loop.

With that in mind, consider this (faulty) reimplementation of button1_Click:

private void button1_Click(object sender, RoutedEventArgs e)       {            Task<string> s = LoadStringAsync();            textBox1.Text = s.Result; // warning: buggy        }

There’s an exceedingly good chance that this code will hang your application.  The Task<string>.Result property is strongly typed as a String, and thus it can’t return until it has the valid result string to hand back; in other words, it blocks until the result is available.  We’re inside of button1_Click then blocking for LoadStringAsync to complete, but LoadStringAsync’s implementation depends on being able to post code asynchronously back to the UI to be executed, and the task returned from LoadStringAsync won’t complete until it does.  LoadStringAsync is waiting for button1_Click to complete, and button1_Click is waiting for LoadStringAsync to complete. Deadlock!

This problem can be exemplified easily without using any of this complicated machinery, e.g.:

private void button1_Click(object sender, RoutedEventArgs e) {     var mre = new ManualResetEvent(false);     SynchronizationContext.Current.Post(_ => mre.Set(), null);     mre.WaitOne(); // warning: buggy }

Here, we’re creating a ManualResetEvent, a synchronization primitive that allows us to synchronously wait (block) until the primitive is set.  After creating it, we post back to the UI thread to set the event, and then we wait for it to be set.  But we’re waiting on the very thread that would go back to the message loop to pick up the posted message to do the set operation.  Deadlock.

The moral of this (longer than intended) story is that you should not block the UI thread.  Contrary to Nike’s recommendations, just don’t do it.  The new async language functionality makes it easy to asynchronous wait for your work to complete.  So, on your UI thread, instead of writing:

Task<string> s = LoadStringAsync();       textBox1.Text = s.Result; // BAD ON UI

you can write:

Task<string> s = LoadStringAsync();       textBox1.Text = await s; // GOOD ON UI

Or instead of writing:

Task t = DoWork();       t.Wait(); // BAD ON UI

you can write:

Task t = DoWork();       await t; // GOOD ON UI

This isn’t to say you should never block.  To the contrary, synchronously waiting for a task to complete can be a very effective mechanism, and can exhibit less overhead in many situations than the asynchronous counterpart.  There are also some contexts where asynchronously waiting can be dangerous. For these reasons and others, Task and Task<TResult> support both approaches, so you can have your cake and eat it too.  Just be cognizant of what you’re doing and when, and don’t block your UI thread.

(One final note: the Async CTP includes the TaskEx.ConfigureAwait method.  You can use this method to suppress the default behavior of marshaling back to the original synchronization context.  This could have been used, for example, in the LoadStringAsync method to prevent those awaits from needing to return to the UI thread.  This would not only have prevented the deadlock, it would have also resulted in better performance, because we now no longer need to force execution back to the UI thread, when nothing in that method actually needed to run on the UI thread.)

http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

http://blogs.msdn.com/b/pfxteam/archive/2011/10/02/10219048.aspx

http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx

转载于:https://www.cnblogs.com/oliverblogs/p/3583973.html

相关文章:

传智播客java基础的习题_传智播客java基础班(集合与IO)阶段测试题

本帖最后由 zhaodecang 于 2016-6-8 19:38 编辑单选题&#xff1a;(每道题目2分)1. ArrayList类的底层数据结构是( )a) 数组结构b) 链表结构 c) 哈希表结构 d) 红黑树结构2. LinkedList类的特点是( )a) 查询快b) 增删快c) 元素不重复 d) 元素自然排序3. Vector类的特点…

$@ 与 $* 差在哪?

$ 与 $* 差在哪&#xff1f; 要说 $ 与 $* 之前&#xff0c;需得先从 shell script 的 positional parameter 谈起...我们都已经知道变量(variable)是如何定义及替换的&#xff0c;这个不用再多讲了。但是&#xff0c;我们还需要知道有些变量是 shell 内定的&#xff0c;且其名…

[源码和文档分享]基于Netty和WebSocket的Web聊天室

一、背景 伴随着Internet的发展与宽带技术的普及&#xff0c;人们可以通过Internet交换动态数据&#xff0c;展示新产品&#xff0c;与人进行沟通并进行电子商务贸易。作为构成网站的重要组成部分&#xff0c;留言管理系统为人们的交流提供了一个崭新的平台。同时&#xff0c;聊…

t-tcpdump

文章目录写入和读取数据包抓取数据包抓取指定网卡流量指定数据的输出格式数据包抓取的方向输出信息的详细程度的可控选项抓取指定协议的数据包表达式介绍逻辑连接符的使用type的确定写入和读取数据包 在工作或者生活中的网络故障排除时最有力的方式就是抓包分析网络状况&#…

java jdk 8u111_8u111-jdk-alpine在java开发中的NullPointerException错误解决方案

问题描述在部署一个验证码服务的容器服务时遇到了一个空指针错误&#xff0c;错误代码为&#xff1a;java.lang.NullPointerExceptionat sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguratio…

sprintf函数做什么用?

sprintf函数原型为 int sprintf(char *str, const char *format, ...)。作用是格式化字符串&#xff0c;具体功能如下所示&#xff1a; &#xff08;1&#xff09;将数字变量转换为字符串。 &#xff08;2&#xff09;得到整型变量的16进制和8进制字符串。 &#xff08;3&#…

Yii学习笔记【3】

加载控制器及其方法&#xff1a; 根据route信息&#xff0c;获得当前控制器| 初始化当前控制器&#xff0c;CController::init()&#xff0c;默认为空| 执行当前控制器&#xff0c;CController::run()||----> 创建action&#xff0c;为空则默认为index|得到CInlineAction的实…

验证码相似问题

产生随机验证码时&#xff0c;类似数字1和小写字母l经常容易让人混淆分不清楚&#xff0c; 因此&#xff0c;产生随机验证码时应避免此情况 1&#xff08;一&#xff09;、l&#xff08;哎哦&#xff09;、I &#xff08;哎&#xff09;中三个任意两个或者全部不可同时存在 0&a…

C语言网络编程:accept函数详解

文章目录前言函数描述代码实例如何得到客户端的IP 和 端口号前言 当使用tcp服务器使用socket创建通信文件描述符&#xff0c;bind绑定了文件描述符&#xff0c;服务器ip和端口号&#xff0c;listen将服务器端的主动描述符转为被动描述符进行监听之后&#xff0c;接口accept通过…

java 声明静态类_java静态类声明--java类可以声明为static吗

为了理解static关键字在类声明中的使用&#xff0c;首先我们需要了解类声明。有两种类&#xff0c;一种是top-level class&#xff1b;一种是inner class。Top-level classestop-level class可以被声明为包成员&#xff0c;每一个top-level类对应于一个文件名与类名相同的java文…

单元测试资料汇总

从安装到配置 首先到官网http://www.nunit.org/下载如下图的资料&#xff0c;安装NUnit-2.6.1.msi包。 然后挂在VS2010外部工具这个地方来使用&#xff0c;工具—>外部工具—>添加—>标题&#xff1a;Nunit—>命令&#xff1a;安装路径—>确定。 然后打开Nunit&…

rhel5+nis+autofs+nfs

创建NIS服务器用户&#xff0c;用于客户端登陆 NIS服务器相关包&#xff1a;ypserv、ypbind(在RHEL5中默认已安装)、yp-tools(在RHEL5中默认已安装)。 运行nisdomainname test.com并把加入到如下位置 设置NIS服务器的域名 在NIS环境中将以NIS服务器上的所有用户用于NIS环境中所…

Beta冲刺 (1/7)

Part.1 开篇 队名&#xff1a;彳艮彳亍团队 组长博客&#xff1a;戳我进入 作业博客&#xff1a;班级博客本次作业的链接 Part.2 成员汇报 组员1&#xff08;组长&#xff09;柯奇豪 过去两天完成了哪些任务 熟悉并编写小程序的自定义控件展示GitHub当日代码/文档签入记录接下来…

C语言网络编程:listen函数详解

文章目录前言函数描述代码实例TCP服务器为什么调用listen前言 根据TCP编程模型中我们可以看到之前的socket和bind接口是tcp服务器在为接收客户端的链接做准备&#xff0c;保证tcp的面向字节流&#xff0c;面向连接的可靠通信服务正常进行。接下来的listen端口则为我们进行三次…

MVC页面加载速度优化小记

前言&#xff1a;最近做一个地图展示页面&#xff0c;业务初期没什么问题&#xff0c;运行一阵后报错&#xff1a; Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLeng…

java生成函数excel_java实现在excel中创建及读取公式

操作excel表格用公式来处理数据时&#xff0c;可通过创建公式来运算数据&#xff0c;或通过读取公式来获取数据信息来源。这里使用了java类库(Free Spire.XLS for Java 免费版)获取文件包后&#xff0c;解压&#xff0c;将lib文件夹下的jar文件导入Java程序。如图&#xff1a;(…

实战:使用TCP/IP筛选保护服务器安全

使用TCP/IP筛选保护服务器安全 对于部署在Internet的服务器&#xff0c;安全是必须要考虑的事情。为了降低服务器受***的危险&#xff0c;停止不必要的服务或在本地连接的TCP/IP属性中只打开必要的端口。 如图2-127所示&#xff0c;实验环境为Server的IP地址192.168.1.200&…

python中的协程(二)

协程 1、协程&#xff1a; 单线程实现并发 在应用程序里控制多个任务的切换保存状态 优点&#xff1a; 应用程序级别速度要远远高于操作系统的切换 缺点&#xff1a; 多个任务一旦有一个阻塞没有切&#xff0c;整个线程都阻塞在原地&#xff0c;该线程内的其他的任务都不能执行…

C语言网络编程:bind函数详解

文章目录函数功能函数头文件函数使用函数参数函数举例为什么需要bind函数服务器如何知道客户端的ip和端口号htons函数htons兄弟函数htonl,ntohs,ntohl为什么要进行端口的大小端序的转换inet_addr函数函数功能 bind API能够将套接字文件描述符、端口号和ip绑定到一起 注意&…

java flex 图片上传_flex上传图片到java服务器

今天弄flex上传图片到java&#xff0c;现在弄成功&#xff0c;中间也经常一点小波折&#xff0c;现记录一下。重点在java侧的实现。flex侧&#xff1a;文件上载到在url参数中传递的URL。该URL必须是配置为接受上载的服务器脚本。Flash Player使用HTTP POST方法上载文件。处理上…

开发者怎么样做到盈利

开发者如何赚钱? 不可回避的一点就是&#xff0c;开发者的产品要有足够好的用户体验。假设你会做手机游戏&#xff0c;那么把手游做好了之后用户的粘性很大&#xff0c;如果你做应用&#xff0c;那么你的应用下载会对用户产生有价值的东西。 其实如果你的产品真的有价值&#…

如何在Windows Azure VM上的SQL Server和Windows Azure SQL Database两者中做出选择

作者信息&#xff1a;本篇文章是由SQL Server Cloud Infrastructure Team的 Madhan Arumugam 和 Guy Bowerman共同著作。 简介 把SQL 数据托管在哪里&#xff0c;Windows Azure 为您提供了两个选择&#xff0c;VM上的SQL Server&#xff08;以下简称 SQL/VM&#xff09;和 Wind…

C语言网络编程:socket函数

函数描述 头文件 <sys/types.h> <sys/socket.h> 函数使用int socket(int domain, int type, int protocol); 函数功能&#xff1a;创建一个通信的终点&#xff0c;并返回一个文件描述符来代表通信的终点 函数参数&#xff1a; a. domain 代编当前创建的socket文…

python excel web_使用python在WEB页面上生成EXCEL文件

近日写的一个程序需要在WEB服务器上生成EXCEL文件供用户下载&#xff0c;研究了一下找到了以下比较可行的实现方案&#xff0c;下面以web.py为例&#xff0c;把相关代码贴出来供大家参考&#xff1a;首先需要下载生成EXCEL的模块&#xff0c;推荐使用xlwtimport xlwtimport Str…

dateTimePicker编辑状态下,取值不正确的问题

当对dateTimePicker进行编辑&#xff0c;回车&#xff0c;调用函数处理dateTimePicker的value值时&#xff0c;其取值结果是你编辑之前的值&#xff0c;而不是你编辑后的值&#xff0c;虽然dateTimePicker.text的值是编辑后的值&#xff0c;但使用起来不方便&#xff0c;因此暂…

RMAN Backups

oracle 主要的备份工具 RMAN 其中&#xff0c;open database backup, 不需要把数据库设置成backup状态, RMAN reads a block until a consistent read is obtained. 看来备份比较重要的三种文件分别是, data file, control file, archivelog file. Types of Recovery Manager B…

异步使用委托delegate --- BeginInvoke和EndInvoke方法

当我们定义一个委托的时候&#xff0c;一般语言运行时会自动帮委托定义BeginInvoke 和 EndInvoke两个方法&#xff0c;这两个方法的作用是可以异步调用委托。 方法BeginInvoke有两个参数&#xff1a; AsyncCallBack&#xff1a;回调函数&#xff0c;是一个委托&#xff0c;没有…

C语言网络编程:TCP编程模型

编程模型 TCP编程模型如下 TCP服务器的工作过程如下&#xff1a; 服务器创建一个专门的“文件描述符”来监听来自客户端的“三次握手”&#xff0c;然后建立链接链接建立成功后&#xff0c;服务器会分配一个专门的“通信文件描述符”&#xff0c;用于实现与该客户端的通信 …

九度 1553:时钟(模拟题)

题目描述&#xff1a;如图&#xff0c;给定任意时刻&#xff0c;求时针和分针的夹角(劣弧所对应的角)。 输入&#xff1a;输入包含多组测试数据&#xff0c;每组测试数据由一个按hh:mm表示的时刻组成。 输出&#xff1a;对于每组测试数据&#xff0c;输出一个浮点数&#xff0c…

python3.7.4安装教程桌面_Python 3.7.4 for Windows的安装

一、Python简介Python是一款通用型的计算机程序设计语言&#xff0c;Python对编程人员来讲是一款很是有利的工具&#xff0c;可让您快速编写代码&#xff0c;并且代码运行速度很是快。Python具备很是简捷而清晰的语法特色&#xff0c;适合完成各类高层任务&#xff0c;几乎能够…