![](/img/trans.png)
[英]How to wait for a return value from an event handler without blocking main UI thread
[英]How to wait in main form for an event fired and completed in another thread without blocking the UI
對於需要提供背景信息的一些代碼,我有一個實際的問題。
在我的winform項目中,我添加了一個參考(Siemens.Sinumerik.Operate.Services.dll)以設置,獲取和監視NC(數控)磨床的某些值。 使用一些特定的值,我可以告訴機器進行一些工作(移動研磨工具等)。 機器完成工作后,它會通過另一個值給我反饋。 在我的winform項目中,我可以使用所謂的熱鏈接監視那些事件。 因此,這意味着如果NC機器更改值,則會在我的winform項目中觸發一個事件。
我的目標是通過設置一些值來開始工作,然后等待機器的答復。
因此,在我的主窗體中,我有一個按鈕單擊事件,該事件啟動了NC機器的作業,並在我的主線程中運行。 在我開始工作之后,主線程需要等待,直到熱鏈接向我提供反饋並且事件結束為止。 問題是我必須在Siemens文檔之后定義此熱鏈接。 每個熱鏈接都在不同的線程中運行並在其自己的類中運行。 現在,我用AutoResetEvent阻止了主線程和UI,直到觸發了另一個線程中的事件並完成了一個類的工作。 問題是用戶界面被阻止,這是我不允許的。
所以我的問題是:如何在主線程中等待事件在另一個線程和類中觸發並完成而又不會阻塞UI?
主要形式的按鈕單擊事件:
private static AutoResetEvent _waitHandle = new AutoResetEvent(false);
// Button Click event that sets the values to cause NC machine to operate
private void cmd_StartDauertest_Click(object sender, EventArgs e)
{
mySWE.SWETauschauftrag();
_waitHandle.WaitOne();
// more work will be done...
}
名為“ Schnellwechseleinheit.cs”的類,該類具有在NC機器的值更改時觸發的事件。 該課程分為兩部分。 一半在類中執行,另一部分在frm main中執行,但仍在“ Schnellwechseleinheit.cs”類的線程中執行。
“ Schnellwechseleinheit.cs”中的課程的前半部分:
class Schnellwechseleinheit
{
public delegate void HotlinkSWEHasChanged();
public event HotlinkSWEHasChanged HotlinkSWEChanged;
DataSvc svc_initSWEHotlink = null;
Guid guid_initSWEHotlink;
/// <summary>
/// Creates the "hotlink for the machine"
/// </summary>
public void initSWEHotlink()
{
DataSvc svc_SWEInit = null;
svc_SWEInit = new DataSvc();
Item SWEInit = new Item(MTU_Settings.Default.SWE_ERGEBNIS);
SWEInit.Value = 0;
svc_SWEInit.Write(SWEInit);
svc_initSWEHotlink = new DataSvc();
Item itemSubscribe = new Item(MTU_Settings.Default.SWE_ERGEBNIS);
guid_initSWEHotlink = svc_initSWEHotlink.Subscribe(OnInitSWEHotlinkChanged, itemSubscribe);
}
/// <summary>
/// This is the event of the Hotlink. Is caused when the value of the NC machine changes
/// </summary>
/// <param name="guid"></param>
/// <param name="item"></param>
/// <param name="Status"></param>
private void OnInitSWEHotlinkChanged(Guid guid, Item item, DataSvcStatus Status)
{
try
{
DataSvc svc_SWEErg = null;
svc_SWEErg = new DataSvc();
Item SWEErg = new Item(MTU_Settings.Default.SWE_ERGEBNIS);
svc_SWEErg.Read(SWEErg);
if (Convert.ToInt16(SWEErg.Value) == 0)
{
writeStatSWE("Reset PLC Variable AMR Ergebnis für Auftragsstart!");
}
else if (Convert.ToInt16(SWEErg.Value) == 1)
{
writeStatSWE("Transportauftrag SWE wurde erfolgreich abgeschlossen!");
}
else
{
writeStatSWE("Transportauftrag SWE wurde von PLC abgelehnt :::: Fehlercode :::: " + SWEErg.Value.ToString());
}
this.HotlinkSWEChanged();
}
catch (Exception ex)
{
writeStatSWE(ex.Message);
}
}
}
main.cs形式的“ Schnellwechseleinheit.cs”類的下半部分:
// Creating the object of the class "Schnellwechseleinheit" and adding the event
mySWE = new Schnellwechseleinheit();
mySWE.initSWEHotlink();
mySWE.HotlinkSWEChanged += mySWEHotlinkChanged;
/// <summary>
/// Second half of the hotlink (the event that is added)
/// </summary>
private void mySWEHotlinkChanged()
{
if (mySWE.getSWEErg() == 1)
{
Werkzeug WZGetData = new Werkzeug();
MagElements MagDataPocket1 = new MagElements();
MagElements MagDataGreifer1 = new MagElements();
MagElements MagDataGreifer2 = new MagElements();
MagDataPocket1 = WZGetData.getWZData(21);
if (MagDataPocket1 != null)
{
MagDataPocket1 = ToolsInMag[ToolsInMag.FindIndex(x => (x.TN == MagDataPocket1.TN && x.DN == MagDataPocket1.DN))];
MagDataPocket1.ORT = myAMR.WriteORT(21);
ToolsInMag[ToolsInMag.FindIndex(x => (x.TN == MagDataPocket1.TN && x.DN == MagDataPocket1.DN))] = MagDataPocket1;
}
MagDataGreifer1 = WZGetData.getWZData(10);
if (MagDataGreifer1 != null)
{
MagDataGreifer1 = ToolsInMag[ToolsInMag.FindIndex(x => (x.TN == MagDataGreifer1.TN && x.DN == MagDataGreifer1.DN))];
MagDataGreifer1.ORT = myAMR.WriteORT(10);
ToolsInMag[ToolsInMag.FindIndex(x => (x.TN == MagDataGreifer1.TN && x.DN == MagDataGreifer1.DN))] = MagDataGreifer1;
}
MagDataGreifer2 = WZGetData.getWZData(11);
if (MagDataGreifer2 != null)
{
MagDataGreifer2 = ToolsInMag[ToolsInMag.FindIndex(x => (x.TN == MagDataGreifer2.TN && x.DN == MagDataGreifer2.DN))];
MagDataGreifer2.ORT = myAMR.WriteORT(11);
ToolsInMag[ToolsInMag.FindIndex(x => (x.TN == MagDataGreifer2.TN && x.DN == MagDataGreifer2.DN))] = MagDataGreifer2;
}
_waitHandle.Set();
UpdateMagDgv();
}
一種實現方法是在EventArgs
中具有SemaphoreSlim
屬性,該屬性在事件處理程序完成時由事件處理程序發出信號,並由UI等待。
例如,給定默認的Windows Forms應用程序帶有按鈕“ Button1”,請首先定義一個EventArgs類,如下所示:
public sealed class MyEventArgs : EventArgs
{
public MyEventArgs(SemaphoreSlim finished)
{
Finished = finished;
}
public SemaphoreSlim Finished { get; }
}
表單定義了一個事件處理程序,如下所示:
public event EventHandler<MyEventArgs> SomeEvent;
按鈕單擊處理程序為:
async void Button1_Click(object sender, EventArgs e)
{
var handler = SomeEvent;
if (handler == null)
return;
Text = "Waiting for event to be handled.";
button1.Enabled = false;
using (var sem = new SemaphoreSlim(0, 1))
{
var args = new MyEventArgs(sem);
handler(this, args);
await sem.WaitAsync();
}
Text = "Finished waiting for event to be handled.";
button1.Enabled = true;
}
然后,可以像這樣(從Program實現)預訂並處理事件:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (var form = new Form1())
{
form.SomeEvent += onSomeEvent;
Application.Run(form);
}
}
static void onSomeEvent(object sender, MyEventArgs e)
{
Task.Run(() => handleEvent(e));
}
static void handleEvent(MyEventArgs e)
{
Thread.Sleep(4000);
e.Finished.Release();
}
}
請注意,處理程序如何啟動新任務來處理事件,並向信號量發出信號以指示事件完成的時間。
如果運行該程序並單擊按鈕,標題將變為“正在等待事件處理”。 4秒鍾,然后更改為“完成等待事件處理”。
在這段時間內,UI不會被阻塞,因為它正在等待信號量。
另外,如果處理事件的方法是同步的,則可以通過“任務”運行它並等待任務,而無需使用信號燈。
事件處理程序將很簡單:
public event EventHandler<EventArgs> SomeEvent;
按鈕單擊處理程序將是:
async void Button1_Click(object sender, EventArgs e)
{
var handler = SomeEvent;
if (handler == null)
return;
Text = "Waiting for event to be handled.";
button1.Enabled = false;
await Task.Run(() => handler(this, EventArgs.Empty));
Text = "Finished waiting for event to be handled.";
button1.Enabled = true;
}
事件處理程序本身可以在類Program中實現,如下所示:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApp3
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (var form = new Form1())
{
form.SomeEvent += onSomeEvent;
Application.Run(form);
}
}
static void onSomeEvent(object sender, EventArgs e)
{
Thread.Sleep(4000);
}
}
}
當然,這里有多種獲取您想要做的事的方法。
我首先想到的是使您的方法private void cmd_StartDauertest_Click(object sender, EventArgs e)
async
看起來像這樣: private async void cmd_StartDauertest_Click(object sender, EventArgs e)
在方法內部,您可以使用await關鍵字。 您的方法主體將如下所示:
await Task.Run(() =>
{
mySWE.SWETauschauftrag();
_waitHandle.WaitOne();
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.