C# 工控开发日记001:Winform报错_线程间操作无效

2022-07-28,,,,

传送门

  • 原程序架构
  • 出现的问题
  • 解决思路
  • 总结

原程序架构

此程序有一功能是通过ListBox记录按钮操作,包含【Initialize】、【Disable】、【Read Volt】和【Keep Read】。
其中【Initialize】、【Disable】、【Read Volt】按钮均是单次读取,并没有使用线程,所以能直接在按钮事件中对ListBox执行Iteam.Add()方法。如下举例Disable按钮的事件处理,应该很好理解。

private void btnDisable_Click(object sender, EventArgs e)
{
    bool ret = My9113CardDevice.CardRelease();//卡Release,返回值表示是否成功。
    if (ret)
    {
        ShowActionAtListBox("Release Card Finish!",MsgType.Action);//MsgType是个枚举,这里可忽略其作用。
    }
    else
    {
        ShowActionAtListBox("Release Card Failed!",MsgType.Error);
    }
}

ListBox项目添加信息的方法。

private void ShowActionAtListBox(string ActionString , MsgType msgType)
{
     string[] MT = new string[] { "  _ Action_ : ", " _ Warning_ : ", "   _ Alarm_ : ", "   _ Error_ : ", "   _ Other_ : " };
     this.lbShowMsg.Items.Add(DateTime.Now.ToString() + MT[(int)msgType] + ActionString);
     this.lbShowMsg.SelectedIndex = this.lbShowMsg.Items.Count - 1;
}

但对于【Keep Read】按钮,为了用户体验(连续读取的时候软件不卡死),就创建线程执行方法:每隔500ms对ListBox执行Iteam.Add()。【Keep Read】按钮事件处理如下。

private void btnKepReadVolt_Click(object sender, EventArgs e)
{
    if (iskeepReadVolt==false)
    {
        Thread thrKeepReadVolt = new Thread(new ThreadStart(KepReadVoltMeoth));
        thrKeepReadVolt.Start();
        iskeepReadVolt = true; 
    }
    else
    {
        iskeepReadVolt = false;
    }
    this.btnKepReadVolt.Text = iskeepReadVolt ? "Stop Read" : "Keep Read";
    
}

线程所调用的方法如下:

private void KepReadVoltMeoth()
{
    while (iskeepReadVolt)
    {
        ShowActionAtListBox("Read Vel", MsgType.Action);
        Thread.Sleep(500);
    }          
}

想法很理想。
不是连续执行的按钮,直接顺序执行,也不影响用户体验。
需要连续执行的按钮,按下后→创建一个线程→线程连续执行。
然而出错了。

出现的问题

当按下【Initialize】、【Disable】、【Read Volt】按钮能顺利显示相应信息。但当按下【Keep Read】。出现如下报错:线程间操作无效

解决思路

创建委托,用委托创建一个事件。然后ListBox订阅这个事件。
在Button KeyDown事件中创建线程,线程用异步调用的方式触发事件。事件的订阅者调用ListBox的方法。
完美。

代码如下:

//创建委托和委托类型的事件。
public delegate void dlgTemp(string ActionString, MsgType msgType);
public event dlgTemp EventTemp;
public WF9113()
{            
	InitializeComponent();
	//订阅事件,事件处理方法为ListBox.Item.Add();
	this.EventTemp += ShowActionAtListBox;
}
//相应按键的事件代码不用修改
private void btnKepReadVolt_Click(object sender, EventArgs e)
{
    if (iskeepReadVolt==false)
    {
    	//此处启动线程
        Thread thrKeepReadVolt = new Thread(new ThreadStart(KepReadVoltMeoth));
        thrKeepReadVolt.Start();
        iskeepReadVolt = true; 
    }
    else
    {
        iskeepReadVolt = false;
    }
    this.btnKepReadVolt.Text = iskeepReadVolt ? "Stop Read" : "Keep Read";
    
}
private void KepReadVoltMeoth()
{
    while (iskeepReadVolt)
    {
    	//使用异步调用的方式调用EventTemp事件。从而触发KepReadVoltMeoth方法
        this.lbShowMsg.Invoke(EventTemp, "Read Vel", MsgType.Action);
        Thread.Sleep(500);
    }          
}

效果如下,此时在连续读取的情况下也可以对其他按钮进行操作。

总结

此文最后编辑于2020年10月25日,只对现象进行大概表述,临时做一个问题处理记录。
一些原理和技术要点,肯定有不妥、错漏,日后再补充,也希望朋友能提出宝贵意见和建议。

本文地址:https://blog.csdn.net/CSDN_PEN/article/details/109271417

《C# 工控开发日记001:Winform报错_线程间操作无效.doc》

下载本文的Word格式文档,以方便收藏与打印。