Conmajia © 2012, 2018

Published on August 5th, 2012

Updated on February 2nd, 2019

Introduction

While working on background threads, you may encounter updating values of frontend GUI controls. Then you probably will face one particular problem: invalid operation between threads. As shown in figure 1, this issue throws an InvalidOperationException. It occurs every time when accesses properties/methods from other threads but the one which owns them.

Fig. 1 Invalid Cross-Thread Call (screenshot)

In .NET Framework, version 2.0 as I refer, every Control class contains an InvokeRequired property and an Invoke method to accomplish cross-thread operations. Some typical call code is listed below.

public void DoWork() {
if (control.InvokeRequired) {
control.Invoke(DoWork);
}
else {
// do work
}
}

My Approach

I wrote a helper class InvokeHelper which granted me the power to access assets between different threads. The class has 3 methods:

  1. Invoke() - to call methods of a control.
InvokeHelper.Invoke(<control>, "<method>"[, <param1>[,<param2>,...]]);
  1. Get() - to get properties of a control.
InvokeHelper.Get(<control>, "<property>");
  1. Set() - to set properties of a control.
InvokeHelper.Set(<control>, "<property>", <value>);

Demonstration

In the demo, I used a forever looping background thread (t) to show how the InvokeHelper helps threads to interact. Thread t updates the frontend thread (the GUI) every 500 milliseconds.

Thread t;
private void button1_Click(object sender, EventArgs e) {
if(t == null) {
t = new Thread(multithread);
t.Start();
label4.Text = string.Format("Thread state:\n{0}", t.ThreadState.ToString());
}
}
public void DoWork(string msg) {
this.label3.Text = string.Format("Invoke method: {0}", msg);
}
int count = 0;
void multithread() {
while(true) {
InvokeHelper.Set(this.label1, "Text", string.Format("Set value: {0}", count));
InvokeHelper.Set(this.label1, "Tag", count);
string value = InvokeHelper.Get(this.label1, "Tag")
.ToString();
InvokeHelper.Set(this.label2, "Text", string.Format("Get value: {0}", value));
InvokeHelper.Invoke(this, "DoWork", value);
Thread.Sleep(500);
count++;
}
}

Results shown in animated figure 2. Obviously, the frontend is not blocked despite t never stops.

Fig. 2 InvokeHelper Demonstration (animation)

Other Approaches

There is a built-in option in the VisualStudio IDE that disables the cross-thread access check: CheckForIllegalCrossThreadCalls. I found it slightly slower than my helper class. Figure 3 shows the test result.

Fig. 3 Test Result for CheckForIllegalCrossThreadCalls

The whole procedure of the test is recorded in figure 4. (The video lasts for 8'51")

Fig. 4 Test Video for CheckForIllegalCrossThreadCalls (8'51")

Appendix

Source code of the InvokeHelper is listed below. Zipped .NET project files: Download


public class InvokeHelper {
private delegate object MethodInvoker(Control control, string methodName, params object[] args);
private delegate object PropertyGetInvoker(Control control, object noncontrol, string propertyName);
private delegate void PropertySetInvoker(Control control, object noncontrol, string propertyName, object value);
private static PropertyInfo GetPropertyInfo(Control control, object noncontrol, string propertyName) {
if (control != null && !string.IsNullOrEmpty(propertyName)) {
PropertyInfo pi = null;
Type t = null;
if (noncontrol != null) t = noncontrol.GetType();
else t = control.GetType();
pi = t.GetProperty(propertyName);
if (pi == null) throw new InvalidOperationException(string.Format("Can't find property {0} in {1}.", propertyName, t.ToString()));
return pi;
} else throw new ArgumentNullException("Invalid argument.");
}
public static object Invoke(Control control, string methodName, params object[] args) {
if (control != null && !string.IsNullOrEmpty(methodName))
if (control.InvokeRequired) return control.Invoke(new MethodInvoker(Invoke), control, methodName, args);
else {
MethodInfo mi = null;
if (args != null && args.Length > 0) {
Type[] types = new Type[args.Length];
for (int i = 0; i

References

  1. Sergiu Josan, Making Controls Thread-safely, May 2009
  2. vicoB, Extension of safeInvoke, July 2010

The End. \(\Box\)

💈 A Cross-Thread Call Helper Class的更多相关文章

  1. update the UI property cross thread

    this.Invoke((MethodInvoker)delegate { txtResult.Text = sbd.ToString(); // runs on UI thread });

  2. Flink - Checkpoint

    Flink在流上最大的特点,就是引入全局snapshot,   CheckpointCoordinator 做snapshot的核心组件为, CheckpointCoordinator /** * T ...

  3. [转]How to display the data read in DataReceived event handler of serialport

    本文转自:https://stackoverflow.com/questions/11590945/how-to-display-the-data-read-in-datareceived-event ...

  4. c#子线程线程中操作窗体更新的报错

    用 在执行上传时,由于操作较长窗体界面卡住,于是用task解决 Task t1 = new Task(manage.UploadData); t1.Start(); 结果不卡了,程序也传完了,运行到更 ...

  5. 进程创建过程详解 CreateProcess

    转载请您注明出处:http://www.cnblogs.com/lsh123/p/7405796.html 0x01 CreateProcessW CreateProcess的使用有ANSI版本的Cr ...

  6. C#委托+回调详解

    今天写不完,明天会接着写的,,,, 学习C#有一段时间了,不过C#的委托+回调才这两天才会用,以前只是知道怎么用.前面的一篇文章,函数指针,其实是为这个做铺垫的,说白了委托就相当于C语言中的函数指针, ...

  7. windows中的进程和线程

    今天咱们就聊聊windows中的进程和线程 2016-09-30 在讨论windows下的进程和线程时,我们先回顾下通用操作系统的进程和线程.之所以称之为通用是因为一贯的本科或者其他教材都是这么说的: ...

  8. [MethodImpl(MethodImplOptions.Synchronized)]、lock(this)与lock(typeof(...))

    对于稍微有点经验的.NET开发人员来说,倘若被问及如何保持线程同步,我想很多人都能说好好几种.在众多的线程同步的可选方式中,加锁无疑是最为常用的.如果仅仅是基于方法级别的线程同步,使用System.R ...

  9. Xamarin.Forms客户端第一版

    Xamarin.Forms客户端第一版 作为TerminalMACS的一个子进程模块,目前完成第一版:读取展示手机基本信息.联系人信息.应用程序本地化. 功能简介 详细功能说明 关于TerminalM ...

  10. [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块

    [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块 目录 [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块 0x00 摘要 0x01 前言 0x02 ...

随机推荐

  1. SSH的jar包下载地址

    spring http://repo.spring.io/libs-release-local/org/springframework/spring/ 条理清晰的搭建SSH环境之添加所需jar包 ht ...

  2. bash中声明变量方法

    bash提供了declare命令来声明变量,该命令的基本语法如下: declare attribute variable      其中,attribute表示变量的属性,常用的属性有如下所述.   ...

  3. 有关linux下redis overcommit_memory的问题

    公司的几台Redis服务器出现不明故障,查看Redis日志,发现如下提示: 1 [34145] 01 Jan 17:42:02 # WARNING overcommit_memory is set t ...

  4. linux_RAID

    什么是RAID? 磁盘阵列,把多个磁盘组合成一个磁盘组,在逻辑上看起来就是一块大的磁盘,提供单个物理磁盘的存储量和更高的存储性能,同时提供不同级别的冗余备份的一种技术,不同的RAID技术对应不同级别 ...

  5. Linux指令--mkdir

    本篇博客参照http://www.cnblogs.com/peida/archive/2012/10/25/2738271.html. linux mkdir 命令用来创建指定的名称的目录,要求创建目 ...

  6. git 安装使用

    系统ubuntu14.04 1.安装:sudo  apt-get install git 2.设置name and Email: git config --global user.name " ...

  7. curl错误码说明

    1.得到错误码 $errno=curl_errno($ch); if($errno!=0){ -- } 2.错误码说明 <?php return [ '1'=>'CURLE_UNSUPPO ...

  8. awk处理重复行错误分析

    [root@localhost ~]#cat 0712 YRSD2-1-11 YRSD2-2-18 YRSD1-1-8 YRSD1-1-18 YRSD1-1-20 YRSD1-1-25 YRSD1-2 ...

  9. CSS——类和ID选择器的区别

    1.相同点,可以应用在任何元素. 2.不同点,ID选择器只能在元素里只能分别引用,不能同时引用. 如: <style type="text/css">.stress{( ...

  10. 关于define和const

    1.通过define定义的常量,在C语言里面一般叫宏定义.define的本质是简单的文本替换. 2.const定义一个变量,但是这个变量的值只能在定义的时候赋予,之后就不能被更改了. 如果变量声明中带 ...