设置:
Win10 .NET 4.7.1 / VS2017 .NET 4.5 / C#
级别:
初学者/中级/线程新手
目标:
1:由计时器类触发的硒Web自动化类,以便Web自动化类可以在特定时间与javascript网站交换数据。
2:应该可以将解决方案从WebForms迁移到.NET库(dll)。
问题:
步骤1。计时器类将时间事件发送到Web类中的方法以登录到Internet站点=工作。
步骤2。Web自动化类(WinForms / GUI)尝试从计时器类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();
}
}
}
答案 0 :(得分:1)
当您要从另一个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
}
答案 1 :(得分:0)
此错误是因为更改标签文本,因为标签在另一个线程中,您可以使用此代码
lblAccount.Invoke(new EventHandler((s, ee) => { lblAccount.Text = AccountBalance.ToString(); }));
答案 2 :(得分:0)
此解决方案可能仅对我而言有效。如果认为重复,OP可以删除该问题。
第一个目标是使用GUI轻松开发/运行/调试情况。设置属性不会导致跨线程异常。在MessageBox.Show()中显示属性也不会引起异常。因此,在开发/ GUI阶段无需回避任何跨线程问题。
第二个目标是迁移到dll,因此无需干扰GUI线程。
/仍然感谢