怪异的任务。run行为=>回到UI
我已经阅读了很多文章,完成了链接的学习课程,并且仍然被异步 /等待和task.run()()所困惑。
我有一个Winforms应用程序。我想连接到两个数据库,如果连接成功,请阅读大量数据,并使用该数据更新UI。我希望它运行异步,因为连接到数据库和负载需要大量秒,并且我们的一些用户的带宽较低,并且将坐在那里更长的时间。
我不明白的是我的工作正常,然后我开始得到非法的跨线电话例外。现在:我知道我可以使用委托来更新可见性,但这不是我想解决问题的方法。我想了解控制流在哪个线程/同步上下文中的工作方式,以便以后可以更好地设计UI更新,而无需垃圾邮件beginInvoke
或invoke> Invoke
到处都是。
因此,我弄清楚了我所做的事情,倒转了,然后再次工作。但是我不明白为什么它停止工作。让我解释一下:
要使我的应用程序async
,我制作了主要的形式加载事件async,并用它来开启工作:
private async void Main_Load(object sender, EventArgs e)
{
this.actionProgress.Visible = true; \\<= this works fine.
Model = new Model(SiteCode, this);
bool connected = await Model.InitialiseConnectionsAsync().ConfigureAwait(true);
/* Snip StatusLight update etc*/
this.actionProgress.Visible = false; \\<= this started giving me an illegal cross-thread exception.
}
action progress
是tool> tool> tool stripprogressbar
。还有一些其他代码可以根据“连接”结果对其进行更新,但是为了清楚起见,我省略了。
modelInitialiseconnectionsAsync()
如下:
public async Task<bool> InitialiseConnectionsAsync()
{
bool piConnected = await OpenPIConnection().ConfigureAwait(true);
PIConnection.ConnectionStateChanged += PIConnection_ConnectionStateChanged;
bool afConnected = await OpenAFConnection().ConfigureAwait(true);
AFConnection.ConnectionStateChanged += AFConnection_ConnectionStateChanged;
return piConnected && afConnected;
}
以及我想通过task.run()我想发生的两个连接方法 -
public async Task<bool> OpenPIConnection()
{
PIConnection = new PIConnection();
bool result = await Task.Run(() => PIConnection.ConnectToPI()).ConfigureAwait(true);
return result;
}
public async Task<bool> OpenAFConnection()
{
AFConnection = new AFConnection();
bool result = await Task.Run(() => AFConnection.ConnectToAF()).ConfigureAwait(true);
return result;
}
我认为会发生的同步代码,是onirationisemodelconnectionsasync appawn off yock nock nock narry,apk off yock run n run n run eark your进行工作的线程,我的UI将保持响应迅速,然后连接完成后,它将在UI线程中随身携带并更新。
我读了更多文章,与&amp一起播放。如果没有configureawait(),就试图使调用变平(我已经嵌套了任务。所以我回去看看我改变了什么。
更改的是,参数传递给模型
构造函数:Sitecode。在生产中,这将是传递给可执行文件的论点。我所做的更改是检查是否通过了有效的参数,如果没有传递,则为用户选择一个表格。我将此代码放入构造函数中(在解析论点之后):
if (SiteCode == string.Empty)
{
using (SiteSelector siteSelector = new SiteSelector())
{
siteSelector.ShowDialog();
SiteCode = siteSelector.SelectedSite;
}
}
我解开了这个,我的例外消失了!因此,我认为我将其从主构造函数移至main_load()方法...,我的例外远离。
那时,我完全感到困惑,我想我会来这里寻求帮助。我已经“修复”了,但是我不明白为什么,因此我怀疑它可能再次破裂。
感激地收到任何帮助!
编辑: 根据下面的评论,我在Mainform构造函数和MainForm_load事件中检查了HWND和SynchronizationContest,在构造函数中调用了站点表单(导致跨线程异常),并且在_load中称为SiteSelector形式。事件(正常运行)。
siteSelector在构造函数中显示:
Main Form Handle at constructor: 593070, Main Form Synchronisation Context at constructor: System.Windows.Forms.WindowsFormsSynchronizationContext
Main Form Synchronisation Context at constructor, after SiteSelector shown: System.Threading.SynchronizationContext
Main Form Handle at _load: 593070, Main Form Synchronisation Context at _Load: System.Threading.SynchronizationContext
Thread before await: 1, Context before await = System.Threading.SynchronizationContext
Thread after await: 5
System.Threading.SynchronizationContext.Current.**get** returned null.
,网站选择器移至_load事件,
Main Form Handle at constructor: 199726, Main Form Synchronisation Context at constructor: System.Windows.Forms.WindowsFormsSynchronizationContext
Main Form Handle at _load: 199726, Main Form Synchronisation Context at _Load: System.Windows.Forms.WindowsFormsSynchronizationContext
Main Form Synchronisation Context at _Load, after SiteSelector shown: System.Windows.Forms.WindowsFormsSynchronizationContext
Thread before await: 1, Context before await = System.Windows.Forms.WindowsFormsSynchronizationContext
Thread after await: 1, Context after await = System.Windows.Forms.WindowsFormsSynchronizationContext
所以...肯定会产生一定的效果!
I've read lots of articles, completed a linked-in learning course and am still confused by async / await and Task.Run().
I have a WinForms app. I want to connect to two databases, and if the connections are successful, read a lot of data, and use that data to update the UI. I want it to run async because it takes quite a few seconds to connect to the database and load, and some of our users have low bandwidth, and will be sitting there for an even longer time.
The thing I don't understand is that I had it working, and then I started getting illegal cross-thread call exceptions. Now: I know that I can use a delegate to update the visibility, but that is not how I want to solve the problem. I want to understand how the control flow works in which thread / synchronization context, so that I can better design the UI updates later, without spamming BeginInvoke
or Invoke
everywhere.
So I worked out what I'd done, reversed it, and got it working again. But I don't understand why it stopped working. Let me explain:
To make my app async
, I made the Main Form loading event async, and used that to kick-off the work:
private async void Main_Load(object sender, EventArgs e)
{
this.actionProgress.Visible = true; \\<= this works fine.
Model = new Model(SiteCode, this);
bool connected = await Model.InitialiseConnectionsAsync().ConfigureAwait(true);
/* Snip StatusLight update etc*/
this.actionProgress.Visible = false; \\<= this started giving me an illegal cross-thread exception.
}
actionProgress
is a toolStripProgressBar
. There was some other code to update it based on the 'connected' result, but I've omitted that for clarity.
ModelInitialiseConnectionsAsync()
is as follows:
public async Task<bool> InitialiseConnectionsAsync()
{
bool piConnected = await OpenPIConnection().ConfigureAwait(true);
PIConnection.ConnectionStateChanged += PIConnection_ConnectionStateChanged;
bool afConnected = await OpenAFConnection().ConfigureAwait(true);
AFConnection.ConnectionStateChanged += AFConnection_ConnectionStateChanged;
return piConnected && afConnected;
}
And the two connection methods - synchronous code that I want to run asynchronously via Task.Run()
public async Task<bool> OpenPIConnection()
{
PIConnection = new PIConnection();
bool result = await Task.Run(() => PIConnection.ConnectToPI()).ConfigureAwait(true);
return result;
}
public async Task<bool> OpenAFConnection()
{
AFConnection = new AFConnection();
bool result = await Task.Run(() => AFConnection.ConnectToAF()).ConfigureAwait(true);
return result;
}
What I thought would happen is that InitialiseModelConnectionsAsync would happily run off, spawn some background threads to do the work, my UI would remain responsive, and then when the connections are finished, it would carry-on in the UI thread and update.
I read some more articles, played around with & without configureAwait(), tried to flatten the calls (I had nested Task.Run calls), but nothing worked. So I went back to look at what I'd changed.
What changed is the argument passed to the Model
constructor: SiteCode. In production, this will be an argument passed to the executable. The change I made was to check if a valid argument had been passed, and if not, generate a form for the user to select it. I put this code into the Form Constructor (just after the arguments were parsed):
if (SiteCode == string.Empty)
{
using (SiteSelector siteSelector = new SiteSelector())
{
siteSelector.ShowDialog();
SiteCode = siteSelector.SelectedSite;
}
}
I undid this, and my exception went away! So I figured I'd moved it from the Main constructor into the Main_Load() method... and my exception stayed away.
At that point, being thoroughly confused, I figured I'd come here for help. I have 'fixed' it, but I don't understand why, and as such I suspect that it might break again.
Any help gratefully received!
Edit:
As per the comments below, I checked the hWnd and the SynchronizationContest in the MainForm constructor and in the MainForm_Load event, with the SiteSelector Form being called in the constructor (leading to the cross-thread exception) and the SiteSelector Form being called in the _Load event (which works OK).
SiteSelector shown in constructor:
Main Form Handle at constructor: 593070, Main Form Synchronisation Context at constructor: System.Windows.Forms.WindowsFormsSynchronizationContext
Main Form Synchronisation Context at constructor, after SiteSelector shown: System.Threading.SynchronizationContext
Main Form Handle at _load: 593070, Main Form Synchronisation Context at _Load: System.Threading.SynchronizationContext
Thread before await: 1, Context before await = System.Threading.SynchronizationContext
Thread after await: 5
System.Threading.SynchronizationContext.Current.**get** returned null.
With the Site Selector moved to the _Load Event
Main Form Handle at constructor: 199726, Main Form Synchronisation Context at constructor: System.Windows.Forms.WindowsFormsSynchronizationContext
Main Form Handle at _load: 199726, Main Form Synchronisation Context at _Load: System.Windows.Forms.WindowsFormsSynchronizationContext
Main Form Synchronisation Context at _Load, after SiteSelector shown: System.Windows.Forms.WindowsFormsSynchronizationContext
Thread before await: 1, Context before await = System.Windows.Forms.WindowsFormsSynchronizationContext
Thread after await: 1, Context after await = System.Windows.Forms.WindowsFormsSynchronizationContext
So... it is certainly having some effect!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论