[英]Access object in a different thread
設定:
Win10 .NET 4.7.1 / VS2017 .NET 4.5 / C#
水平:
初學者/中級/線程新手
目的:
1:由計時器類觸發的Selenium Web自動化類,以便Web自動化類可以在特定時間與javascript站點交換數據。
2:應該可以將解決方案從WebForms遷移到.NET庫(dll)。
問題:
步驟1.計時器類將時間事件發送到Web類中的方法以登錄Internet站點=工作。
步驟2. Web自動化類(WinForms / GUI)嘗試從計時器類event = event觸發的方法中檢索數據:異常:“調用線程無法訪問該對象,因為另一個線程擁有它。” (從swe翻譯而來)。
我承認我對新的線程領域的術語感到困惑。 另外,我了解一些多線程技術僅對WinForms有效。 由於我的目標是將解決方案遷移到dll,因此這些都不是我的選擇。 我玩過Invoke(),但是據我所知,它只能在WinForms中使用。 指導受到高度贊賞!
網絡自動化課程:
private EdgeDriver driver;
private SeleniumHelper helper;
private WebAutomationTimer timer;
private double account;
public double Account { get => this.account; set => this.account = value; }
public Form1()
{
InitializeComponent();
timer = new WebAutomationTimer(02, 36, 00, 02, 38, 00);
timer.OnLoginTime += Timer_OnLoginTime;
timer.OnLogoutTime += Timer_OnLogoutTime;
}
private void Timer_OnLoginTime()
{
Login();
}
private void Timer_OnLogoutTime()
{
Logout();
}
public bool Login()
{
try
{
// working login code
UpdateLabels();
}
catch (Exception e)
{
}
}
private void UpdateLabels()
{
// EXCEPTION !!!
lblAccount.Text = GetAccount();
// EXCEPTION !!!
}
計時器類:
class WebAutomationTimer
{
public event TimerEvent OnLoginTime;
public event TimerEvent OnLogoutTime;
//public event TimerEvent OnSecond;
private System.Timers.Timer timer;
private DateTime now;
private int loginHour;
private int loginMin;
private int loginSec;
private int logoutHour;
private int logoutMin;
private int logoutSec;
public WebAutomationTimer(int loginHour, int loginMin, int loginSec, int logoutHour, int logoutMin, int logoutSec)
{
timer = new System.Timers.Timer();
timer.Interval = 1000; // 1 sec
timer.Elapsed += Timer_Elapsed;
timer.Start();
this.loginHour = loginHour;
this.loginMin = loginMin;
this.loginSec = loginSec;
this.logoutHour = logoutHour;
this.logoutMin = logoutMin;
this.logoutSec = logoutSec;
}
// Each second event
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
now = DateTime.Now;
//OnSecond();
//login
if (now.Hour == loginHour && now.Minute == loginMin && now.Second == loginSec)
OnLoginTime();
//logout
if (now.Hour == logoutHour && now.Minute == logoutMin && now.Second == logoutSec)
OnLogoutTime();
}
}
}
當您想從另一個Thread
更新View的控件時,它必須顯示錯誤。 因為它是通過UI Thread使用的。 在這種情況下,您必須使用SynchronizationContext
類,或者可以將Delegate發送到App.Current.Dispatcher.BeginInvoke(delegate must be here)
;
SynchronizationContext _context = SynchronizationContext.Current;
private void UpdateLabels()
{
_context.Post(x=>
{
lblAccount.Text = AccountBalance.ToString();
},null),
//...
}
SynchronizationContext的替代方法:
private void UpdateLabels()
{
var action = new Action(() =>
{
lblAccount.Text = AccountBalance.ToString();
});
App.Current.Dispatcher.BeginInvoke(action);
//...
}
他們都是一樣的。
適用於鍵盤事件和鼠標事件的UI線程。 當您App.Current.Dispatcher.BeginInvoke(delegate)
您對“也執行此操作”的UI Thread
說。
另外,您可以假設這樣的UI Thread
while(!thisApplication.Ended)
{
wait for something to appear in message queue
Got something : what kind of this message?
Keyboard/Mouse message --> fire event handler
User BeginInvoke message --> execute delegate
User Invoke message --> execute delegate & post result
}
此錯誤是因為更改標簽文字,因為標簽在另一個線程中,您可以使用此代碼
lblAccount.Invoke(new EventHandler((s, ee) => { lblAccount.Text = AccountBalance.ToString(); }));
該解決方案可能僅對我而言有效。 如果認為重復,OP可以刪除該問題。
第一個目標是使用GUI輕松開發/運行/調試情況。 設置屬性不會導致跨線程異常。 在MessageBox.Show()中顯示屬性也不會引起異常。 因此,在開發/ GUI階段無需交叉線程問題。
第二個目標是遷移到dll,因此無需干擾GUI線程。
/不管怎么說,還是要謝謝你
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.