目录

概述

取消跨线程检查

使用委托异步调用

sync和await

总结

概述

最近在qq群里有一朋友,问起在winform中怎么通过开启线程的方式去处理耗时的操作,比如,查看某个目录下所有的文件,或者符合要求的文件。下班回来,也研究了一下。发现多线程这块有点薄弱,也算是补一补吧。

在winform开发,经常会遇到需要在控件上加载大量数据(也就是常说的耗时操作),这会导致程序出现假死状态,这个时候我们就会想到线程。

在智能客户端应用程序中,这样的线程创建并管理用户界面 (UI),因而称为 UI 线程。

可以将 UI 线程用于所有的处理,其中包括 Web 服务调用、远程对象调用和数据库调用。然而,以这种方式使用 UI 线程通常并不是一个好主意。在大多数情况下,您不能预测调用Web 服务、远程对象或数据库会持续多久,而且在 UI 线程等待响应时,可能会导致 UI 冻结。

通过创建附加线程,应用程序可以在不使用 UI 线程的情况下执行额外的处理。当应用程序调用 Web 服务时,可以使用多线程来防止 UI 冻结或并行执行某些本地任务,以整体提高应用程序的效率。在大多数情况下,您应该坚持在单独的线程上执行任何与 UI 无关的任务。

取消跨线程检查

案例:现在做一个这样的测试项目,我们选择一个目录通过递归的方式,遍历所有的文件,将文件信息,加载到窗体的DataGridView控件上。界面如图所示:

代码

事件参数和委托:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wofy.ThreadDemo
{ /// <summary>
///功能描述 : 事件参数
///开发者 : wolfy
///建立时间 : 2014年07月19日
///修订描述 :
///进度描述 :
///版本号 : 1.0
///最后修改时间: 2014年07月19日
/// </summary>
public class FileMessageEventArgs:EventArgs
{
public FileMessage fileMessage{set;get;}
}
}

FileMessageEventArgs.cs

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wofy.ThreadDemo
{ /// <summary>
///功能描述 : 文件信息委托
///开发者 : wolfy
///建立时间 : 2014年07月19日
///修订描述 :
///进度描述 :
///版本号 : 1.0
///最后修改时间: 2014年07月19日
/// </summary>
public delegate void FileMessageEventHandler(object sender, FileMessageEventArgs e); }

FileMessageEventHandler.cs

文件信息类:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wofy.ThreadDemo
{
/// <summary>
/// 文件信息
/// </summary>
public class FileMessage
{
/// <summary>
/// 序号
/// </summary>
[Description("序号")]
public int intCount { get; set; }
/// <summary>
/// 文件路径
/// </summary>
[Description("文件路径")]
public string strFilePath { set; get; }
/// <summary>
/// 文件名
/// </summary>
[Description("文件名")]
public string strFileName { set; get; }
/// <summary>
/// 文件类型
/// </summary>
[Description("文件类型")]
public string strFileType { set; get; }
}
}

FileMessage.cs

窗体代码:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace Wofy.ThreadDemo
{
/// <summary>
///功能描述 : 文件浏览器主窗口
///开发者 : wolfy
///建立时间 : 2014年07月19日
///修订描述 :
///进度描述 :
///版本号 : 1.0
///最后修改时间: 2014年07月19日
/// </summary>
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
//取消跨线程检查
// Form.CheckForIllegalCrossThreadCalls = false;
}
private event FileMessageEventHandler fileMessageEventHandler;
private void btnSelectPath_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//目录路径
this.txtPath.Text = folderBrowserDialog.SelectedPath;
fileMessageEventHandler += MainForm_fileMessageEventHandler;
Thread thread = new Thread(new ParameterizedThreadStart(GetFiles));
thread.IsBackground = true;
thread.Start(this.txtPath.Text);
} }
/// <summary>
/// 文件信息事件处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
{
FileMessage fileMessage = e.fileMessage;
this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
} private List<string> lstTypes = null;
static object _objLock = new object();
int intFileCount = ;
/// <summary>
/// 递归获得文件信息
/// </summary>
/// <param name="strPath"></param>
/// <returns></returns>
public void GetFiles(object obj)
{
string strPath = obj.ToString();
List<FileMessage> lstFiles = new List<FileMessage>(); //单例创建集合
if (lstTypes == null)
{
lock (_objLock)
{
if (lstTypes == null)
{
lstTypes = GetCheckedFileType();
}
}
}
string[] files = new string[];
if (lstTypes.Count > )
{
foreach (string strType in lstTypes)
{
files = Directory.GetFiles(strPath, "*" + strType);
AddFileMessage(files);
}
}
else
{
files = Directory.GetFiles(strPath);
AddFileMessage(files);
}
string[] strDirs = Directory.GetDirectories(strPath);
for (int i = ; i < strDirs.Length; i++)
{
GetFiles(strDirs[i]);
}
}
/// <summary>
/// 将信息添加到集合
/// </summary>
/// <param name="files"></param>
private void AddFileMessage(string[] files)
{
for (int i = ; i < files.Length; i++)
{
FileInfo fileInfo = new FileInfo(files[i]);
FileMessage fileMessage = new FileMessage();
fileMessage.intCount = intFileCount++;
fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
fileMessage.strFilePath = fileInfo.FullName;
fileMessage.strFileType = fileInfo.Extension;
FileMessageEventArgs e = new FileMessageEventArgs();
e.fileMessage = fileMessage;
this.fileMessageEventHandler(null, e);
}
}
/// <summary>
/// 获得选择的文件类型
/// </summary>
/// <returns></returns>
private List<string> GetCheckedFileType()
{
List<string> lstFileTypes = new List<string>();
foreach (Control control in this.Controls)
{
if (control is CheckBox)
{
CheckBox checkBox = control as CheckBox;
if (checkBox != null && checkBox.Checked)
{
lstFileTypes.Add(checkBox.Text);
}
}
}
return lstFileTypes;
}
/// <summary>
/// 窗体加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainForm_Load(object sender, EventArgs e)
{
//通过反射的方式添加列
Type type = typeof(FileMessage);
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (objs.Length > )
{
DescriptionAttribute attr = objs[] as DescriptionAttribute;
string result = attr.Description;
this.dgViewFiles.Columns.Add(result, result);
}
}
//调整列宽
AutoSizeColumn(dgViewFiles); }
/// <summary>
/// 使DataGridView的列自适应宽度
/// </summary>
/// <param name="dgViewFiles"></param>
private void AutoSizeColumn(DataGridView dgViewFiles)
{
int width = ;
//使列自使用宽度
//对于DataGridView的每一个列都调整
for (int i = ; i < dgViewFiles.Columns.Count; i++)
{
//将每一列都调整为自动适应模式
dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
//记录整个DataGridView的宽度
width += dgViewFiles.Columns[i].Width;
}
//判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
//则将DataGridView的列自动调整模式设置为显示的列即可,
//如果是小于原来设定的宽度,将模式改为填充。
if (width > dgViewFiles.Size.Width)
{
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
}
else
{
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
//冻结某列 从左开始 0,1,2
dgViewFiles.Columns[].Frozen = true;
}
}
}

MainForm.cs

如果上面的代码会报错:

出现这个错误,是因为新开的线程操作UI主线程上的控件导致的。也就有了第一种解决方案,添加如下代码即可解决问题:

  //取消跨线程检查
Control.CheckForIllegalCrossThreadCalls = false;

取消跨线程检测,总感觉心里不爽,它们是线程安全的,这里非得强制去取消,总感觉有什么隐患似的。虽然解决了问题,但是对DataGridView滚动条却无法使用了。这里就有了常规使用的第二种方案,通过委托来实现。

使用委托异步调用

使用委托修改原来的代码:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace Wofy.ThreadDemo
{
/// <summary>
///功能描述 : 文件浏览器主窗口
///开发者 : wolfy
///建立时间 : 2014年07月19日
///修订描述 :
///进度描述 :
///版本号 : 1.0
///最后修改时间: 2014年07月19日
/// </summary>
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private event FileMessageEventHandler fileMessageEventHandler;
Thread thread;
private void btnSelectPath_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//目录路径
this.txtPath.Text = folderBrowserDialog.SelectedPath;
fileMessageEventHandler += MainForm_fileMessageEventHandler;
thread = new Thread(new ParameterizedThreadStart(GetFiles));
thread.IsBackground = true;
thread.Start(this.txtPath.Text);
} }
//委托
private delegate void DelegateSetDataGridView(FileMessage fileMessage);
/// <summary>
///
/// </summary>
/// <param name="fileMessage"></param>
private void SetDataGridView(FileMessage fileMessage)
{
//获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
if (this.dgViewFiles.InvokeRequired)
{
Invoke(new DelegateSetDataGridView(SetDataGridView), new object[] { fileMessage });
}
else
{
this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
} } /// <summary>
/// 文件信息事件处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
{
FileMessage fileMessage = e.fileMessage;
SetDataGridView(fileMessage);
} private List<string> lstTypes = null;
static object _objLock = new object();
int intFileCount = ;
/// <summary>
/// 递归获得文件信息
/// </summary>
/// <param name="strPath"></param>
/// <returns></returns>
public void GetFiles(object obj)
{
string strPath = obj.ToString();
List<FileMessage> lstFiles = new List<FileMessage>(); //单例创建集合
if (lstTypes == null)
{
lock (_objLock)
{
if (lstTypes == null)
{
lstTypes = GetCheckedFileType();
}
}
}
string[] files = new string[];
if (lstTypes.Count > )
{
foreach (string strType in lstTypes)
{
files = Directory.GetFiles(strPath, "*" + strType);
AddFileMessage(files);
}
}
else
{
files = Directory.GetFiles(strPath);
AddFileMessage(files);
}
string[] strDirs = Directory.GetDirectories(strPath);
for (int i = ; i < strDirs.Length; i++)
{
GetFiles(strDirs[i]);
}
}
/// <summary>
/// 将信息添加到集合
/// </summary>
/// <param name="files"></param>
private void AddFileMessage(string[] files)
{
for (int i = ; i < files.Length; i++)
{
FileInfo fileInfo = new FileInfo(files[i]);
FileMessage fileMessage = new FileMessage();
fileMessage.intCount = intFileCount++;
fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
fileMessage.strFilePath = fileInfo.FullName;
fileMessage.strFileType = fileInfo.Extension;
FileMessageEventArgs e = new FileMessageEventArgs();
e.fileMessage = fileMessage;
this.fileMessageEventHandler(null, e);
}
}
/// <summary>
/// 获得选择的文件类型
/// </summary>
/// <returns></returns>
private List<string> GetCheckedFileType()
{
List<string> lstFileTypes = new List<string>();
foreach (Control control in this.Controls)
{
if (control is CheckBox)
{
CheckBox checkBox = control as CheckBox;
if (checkBox != null && checkBox.Checked)
{
lstFileTypes.Add(checkBox.Text);
}
}
}
return lstFileTypes;
}
/// <summary>
/// 窗体加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainForm_Load(object sender, EventArgs e)
{
//通过反射的方式添加列
Type type = typeof(FileMessage);
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (objs.Length > )
{
DescriptionAttribute attr = objs[] as DescriptionAttribute;
string result = attr.Description;
this.dgViewFiles.Columns.Add(result, result);
}
}
//调整列宽
AutoSizeColumn(dgViewFiles); }
/// <summary>
/// 使DataGridView的列自适应宽度
/// </summary>
/// <param name="dgViewFiles"></param>
private void AutoSizeColumn(DataGridView dgViewFiles)
{
int width = ;
//使列自使用宽度
//对于DataGridView的每一个列都调整
for (int i = ; i < dgViewFiles.Columns.Count; i++)
{
//将每一列都调整为自动适应模式
dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
//记录整个DataGridView的宽度
width += dgViewFiles.Columns[i].Width;
}
//判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
//则将DataGridView的列自动调整模式设置为显示的列即可,
//如果是小于原来设定的宽度,将模式改为填充。
if (width > dgViewFiles.Size.Width)
{
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
}
else
{
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
//冻结某列 从左开始 0,1,2
dgViewFiles.Columns[].Frozen = true;
} private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
//窗体关闭是停止线程
thread.Abort();
}
}
}

MainForm.cs

关于Control.Invoke可以参考下面的文章:
http://msdn.microsoft.com/zh-CN/library/system.windows.forms.control.invoke.aspx

http://msdn.microsoft.com/zh-cn/library/zyzhdc6b.aspx

关于Control.InvokeRequire可以参考下面的文章:

http://msdn.microsoft.com/zh-cn/library/system.windows.forms.control.invokerequired.aspx

Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。 因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。 该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。

窗体上的控件只允许创建它们的线程访问,也就是主线程,如果非主线程访问则会发生异常。我们可以借助于控件的InvokeRequired属性来判断该控件目前是否被主线程访问,如果是,返回false。如果不是,再利用Invoke方法找到主线程,让主线程执行访问控件的方法。

async和await

之前在博客园看到async和await方面的文章,就想着使用一下,发现异步加载变得如此简单。

关于async和await可参考

http://www.cnblogs.com/jesse2013/p/async-and-await.html

使用async和await改写上面的代码:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace Wofy.ThreadDemo
{
/// <summary>
///功能描述 : 文件浏览器主窗口
///开发者 : wolfy
///建立时间 : 2014年07月19日
///修订描述 :
///进度描述 :
///版本号 : 1.0
///最后修改时间: 2014年07月19日
/// </summary>
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private event FileMessageEventHandler fileMessageEventHandler;
//Thread thread;
//注意加上async
private async void btnSelectPath_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//目录路径
this.txtPath.Text = folderBrowserDialog.SelectedPath;
fileMessageEventHandler += MainForm_fileMessageEventHandler;
await GetFiles(this.txtPath.Text); //thread = new Thread(new ParameterizedThreadStart(GetFiles));
//thread.IsBackground = true;
//thread.Start(this.txtPath.Text); } }
//委托
private delegate void DelegateSetDataGridView(FileMessage fileMessage);
/// <summary>
///
/// </summary>
/// <param name="fileMessage"></param>
private void SetDataGridView(FileMessage fileMessage)
{
//获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
if (this.dgViewFiles.InvokeRequired)
{
Invoke(new DelegateSetDataGridView(SetDataGridView), new object[] { fileMessage });
}
else
{
this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
} } /// <summary>
/// 文件信息事件处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
{
FileMessage fileMessage = e.fileMessage;
// SetDataGridView(fileMessage);
this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
} private List<string> lstTypes = null;
static object _objLock = new object();
int intFileCount = ;
/// <summary>
/// 递归获得文件信息
/// </summary>
/// <param name="strPath"></param>
/// <returns></returns>
public async Task<List<FileMessage>> GetFiles(object obj)
{
string strPath = obj.ToString();
List<FileMessage> lstFiles = new List<FileMessage>(); //单例创建集合
if (lstTypes == null)
{
lock (_objLock)
{
if (lstTypes == null)
{
lstTypes = GetCheckedFileType();
}
}
}
string[] files = new string[];
if (lstTypes.Count > )
{
foreach (string strType in lstTypes)
{
files = Directory.GetFiles(strPath, "*" + strType);
AddFileMessage(files);
}
}
else
{
files = Directory.GetFiles(strPath);
AddFileMessage(files);
}
string[] strDirs = Directory.GetDirectories(strPath);
for (int i = ; i < strDirs.Length; i++)
{
await GetFiles(strDirs[i]);
}
//创建Task,创建一个新的线程,不然还会出现UI假死的现象
return await Task.Run(() => { return lstFiles; }); }
/// <summary>
/// 将信息添加到集合
/// </summary>
/// <param name="files"></param>
private void AddFileMessage(string[] files)
{
for (int i = ; i < files.Length; i++)
{
FileInfo fileInfo = new FileInfo(files[i]);
FileMessage fileMessage = new FileMessage();
fileMessage.intCount = intFileCount++;
fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
fileMessage.strFilePath = fileInfo.FullName;
fileMessage.strFileType = fileInfo.Extension;
FileMessageEventArgs e = new FileMessageEventArgs();
e.fileMessage = fileMessage;
this.fileMessageEventHandler(null, e);
}
}
/// <summary>
/// 获得选择的文件类型
/// </summary>
/// <returns></returns>
private List<string> GetCheckedFileType()
{
List<string> lstFileTypes = new List<string>();
foreach (Control control in this.Controls)
{
if (control is CheckBox)
{
CheckBox checkBox = control as CheckBox;
if (checkBox != null && checkBox.Checked)
{
lstFileTypes.Add(checkBox.Text);
}
}
}
return lstFileTypes;
}
/// <summary>
/// 窗体加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainForm_Load(object sender, EventArgs e)
{
//通过反射的方式添加列
Type type = typeof(FileMessage);
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (objs.Length > )
{
DescriptionAttribute attr = objs[] as DescriptionAttribute;
string result = attr.Description;
this.dgViewFiles.Columns.Add(result, result);
}
}
//调整列宽
AutoSizeColumn(dgViewFiles); }
/// <summary>
/// 使DataGridView的列自适应宽度
/// </summary>
/// <param name="dgViewFiles"></param>
private void AutoSizeColumn(DataGridView dgViewFiles)
{
int width = ;
//使列自使用宽度
//对于DataGridView的每一个列都调整
for (int i = ; i < dgViewFiles.Columns.Count; i++)
{
//将每一列都调整为自动适应模式
dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
//记录整个DataGridView的宽度
width += dgViewFiles.Columns[i].Width;
}
//判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
//则将DataGridView的列自动调整模式设置为显示的列即可,
//如果是小于原来设定的宽度,将模式改为填充。
if (width > dgViewFiles.Size.Width)
{
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
}
else
{
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
//冻结某列 从左开始 0,1,2
dgViewFiles.Columns[].Frozen = true;
} private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
//窗体关闭是停止线程
// thread.Abort();
}
}
}

MainForm.cs

结果

总结

第一种方式虽然一个属性可以解决跨线程的问题,但是并不完美,造成DataGridView滚动条无法使用。第二种是最常见的解决线程间操作的解决办法。第三种方式如果直接返回List<FileMessage> ,则界面仍然会有假死,无法移动的现象,应该是await之后并没有创建新的线程造成的,可以通过下面代码方式解决,如果数据量非常大,仍然会瞬间有卡顿的现象(只是看了一篇文章,出于好奇把这种方式列出来了,也算是提供一个跨线程操作UI控件的一个思路吧,不过从代码量看其实实现变的更简单了)。

  //创建Task,创建一个新的线程,不然还会出现UI假死的现象
return await Task.Run(() => { return lstFiles; });

具体细节可参考:

http://www.cnblogs.com/jesse2013/p/async-and-await.html

代码:链接:http://pan.baidu.com/s/1pJK5RJl 密码:avx1

[Winform]线程间操作无效,从不是创建控件的线程访问它的几个解决方案,async和await?的更多相关文章

  1. 线程间操作无效: 从不是创建控件“”的线程访问它~~~的解决方法~ 线程间操作无效: 从不是创建控件“Control Name'”的线程访问它问题的解决方案及原理分析

    看两个例子,一个是在一个进程里设置另外一个进程中控件的属性.另外一个是在一个进程里获取另外一个进程中控件的属性. 第一个例子 最近,在做一个使用线程控制下载文件的小程序(使用进度条控件显示下载进度)时 ...

  2. 线程使用中常见的错误-“System.InvalidOperationException”线程间操作无效: 从不是创建控件“ ”的线程访问它。

    “System.InvalidOperationException”类型的未经处理的异常在 System.Windows.Forms.dll 中发生 其他信息: 线程间操作无效: 从不是创建控件“la ...

  3. wusir 线程间操作无效: 从不是创建控件“”的线程访问它 解决办法

    利用FileSystemWatcher设计一个文件监控系统时,如果一个文件被修改或者新建,则文件修改事件会被多次触发而产生多条信息.为了将一个文件被修改一次而产生的多条信息归结为一条,在设计中新开了一 ...

  4. 黄聪:C#“多线程线程间操作无效: 从不是创建控件的线程访问它。”,跨线程修改控件属性解决方案

    解决方案就是使用代理,在代理中调用主线程的方法来控制控件 /// <summary> /// 声明代理 /// </summary> delegate void SetText ...

  5. 线程间操作无效: 从不是创建控件“textBox2”的线程访问它

    如何:对 Windows 窗体控件进行线程安全调用 线程间操作无效: 从不是创建控件的线程访问它的三种方法 如果使用多线程处理来提高 Windows 窗体应用程序的性能,则你必须确保以线程安全的方式调 ...

  6. 线程间操作无效: 从不是创建控件“textBox2”的线程访问它

    如何:对 Windows 窗体控件进行线程安全调用 线程间操作无效: 从不是创建控件的线程访问它的三种方法 如果使用多线程处理来提高 Windows 窗体应用程序的性能,则你必须确保以线程安全的方式调 ...

  7. 线程间操作无效: 从不是创建控件“button1”的线程访问它。

    .net2后是不能跨线程访问控件的.,窗体上的控件是当前线程创建的,当用户异步执行一个方法:在该方法中给窗体上的控件赋值,记住:当执行一个异步委托的时候,其实 就是开了一个线程去执行那个方法,这样就会 ...

  8. C#: 线程间操作无效: 从不是创建控件“dataGridView”的线程访问它

    最近在修改自动化小工具,用多线程来解决后台拷贝导致WinForm界面卡死的情况,但是遇到过错:线程间操作无效: 从不是创建控件“dataGridView”的线程访问它. 这是因为在多线程程序中,新创建 ...

  9. C#报错"线程间操作无效: 从不是创建控件“XXX”的线程访问它"--解决示例

    C# Winform程序中,使用线程对界面进行更新需要特殊处理,否则会出现异常“线程间操作无效: 从不是创建控件“taskView”的线程访问它.” 在网文“http://www.cnblogs.co ...

  10. 关于“线程间操作无效: 从不是创建控件’textBox1‘的线程访问它”异常的解决方法

    线程间操作无效: 从不是创建控件“textBox1”的线程访问它 背景:通过一个辅助线程计算出的一个值赋给textBox1.text;解决办法:1.直接在窗体的构造函数中加:System.Window ...

随机推荐

  1. 自动化测试===Macaca环境搭建,自我总结

    安装jdk 安装安卓sdk(打开sdk的时候出现问题linux===启动sdk manager下载配置sdk的时候报错的解决办法) 安装gradle,配置环境变量(MACACA===gradle下载和 ...

  2. java8新特性视频、spring4.0视频讲解,javaee基础知识讲解等网址汇总

    1.http://ke.atguigu.com/     海量视频首页 2.http://ke.atguigu.com/course/56    java8新特性学习地址

  3. C++中string类的方法

    C++ string类的方法 具体每个方法怎么使用,可以参考相应的链接. 总的链接为http://www.cplusplus.com/reference/string/string/(C++参考文档) ...

  4. IE11中实现颜色渐变

    background: -ms-linear-gradient(left,#daa23e,#ad7f27); 下面是css3中颜色渐变对各个浏览器的写法:background: -webkit-lin ...

  5. django开发项目实例3--用session是实现简单的登陆、验证登陆和注销功能

    如果你的网页不是纯阅读型的,那么你很有可能希望在用户打开某些界面的时候需要验证用户是否登陆的信息, 虽然django里面有自带的一些user的类,但我看不懂,并且自己实现也不是很难,下面和大家分享一下 ...

  6. Storm实战常见的问题

    该文档为实实在在的原创文档,转载请注明: http://blog.sina.com.cn/s/blog_8c243ea30101k0k1.html 类型 详细 备注 该文档是群里几个朋友在storm实 ...

  7. electron调用C#应用程序实现串口通信

    最近转入零售行业开发了一系列产品,包含便利店收银软件.会员系统.供应链系统.为了追赶潮流,收银软件使用了electron平台开发,界面效果.开发效率确实不错:但是涉及到串口通讯时遇到了麻烦,elect ...

  8. mongo connect BI 连接至Power BI

    第一步:安装mongodb服务 官网地址:https://www.mongodb.com/download-center?jmp=nav#community mongodb的安装请参考:http:// ...

  9. 单断言VS多断言

    STST 想和大家讨论一下,一个测试用例里只做一个断言还是一个用例里做多个相关的断言 比如有一个查询函数Query(id) 返回[姓名,性别,年龄] 那么是在一个测试用例里对这三个属性进行断言好? 还 ...

  10. Python安装scrapy提示 error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++

    error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools&quo ...