我正在尝试让一个控件执行以下操作:
在第一次单击start时显示文本:每30ms显示一个字符。在第二个按钮上,单击cancel the process并立即显示全文。
这是我通过BeginInvoke()传递给控件的方法:
DBX_Dialogue.running = true;
for (int i = 0; i < line.text.Length; i++)
{
Text += line.text[i];
Thread.Sleep(30);
if (DBX_Dialogue.cancel)
{
Text = line.text;
DBX_Dialogue.running = false;
break;
}
}
DBX_Dialogue.running = false;在主窗口的OnClick()方法中,我这样做:
if (DBX_Dialogue.running)
{
DBX_Dialogue.cancel = true;
return;
}
else
{
DelWrite delWrite = WriteLine;
DBX_Dialogue.BeginInvoke(delWrite);
}然而,DBX_Dialogue.cancel从来没有被设置为true,所以我想我的方法实际上并没有异步运行--我做错了什么?
发布于 2020-12-17 09:10:14
简而言之,您正在调用的Control.BeginInvoke()方法将调用UI线程上的委托。一旦委托被调用,在该委托的目标完成执行之前,UI线程将无法执行其他代码。
因此,如果您引用的OnClick()方法被认为是处理该UI线程拥有的控件中的click事件,则该事件处理程序无法执行,因为UI线程在处理委托调用时被卡住了。
还有另一个为任何委托类型定义的BeginInvoke()方法。该线程将对线程池中的委托调用进行排队。可能这就是你要打给他的电话?它看起来像这样:
delWrite.BeginInvoke();当然,当您这样做时,您将遇到从线程池线程访问UI对象的问题。然后,您需要在那里使用Control.Invoke(),只针对那些特定的语句。在这个网站上有很多讨论这项技术的问题和答案。
你的问题缺乏很多细节,可以帮助我们其他人理解整个场景并提供更好的建议。事实是,代码的基本结构看起来很可疑,而且几乎可以肯定有一种更好的方法,它不需要调用任何BeginInvoke()方法。但是,如果没有更多的细节,就不可能确切地知道这会是什么样子。您可以查看任务并行库和async/await等内容,以了解协调UI线程异步工作的现代方法。
这里有一个大胆的猜测,关于你正在尝试做的事情的一个替代的,现代的实现会是什么样子:
private CancellationTokenSource tcs;
async Task Run(CancellationToken token)
{
DBX_Dialogue.running = true;
try
{
for (int i = 0; i < line.text.Length; i++)
{
Text += line.text[i];
await Task.Delay(30, token);
}
}
catch (TaskCancelledException)
{
Text = line.text;
}
finally
{
DBX_Dialogue.running = false;
}
}
void OnClick(object sender, EventArgs e)
{
if (DBX_Dialogue.running)
{
this.tcs.Cancel();
this.tcs.Dispose();
this.tcs = null;
return;
}
else
{
this.tcs = new CancellationTokenSource();
_ = Run(tcs.Token);
}
}开始时没有代码示例,所以我没有费心编译上面的代码,更不用说实际测试了。它可能需要一些摆弄才能让它像你想要的那样工作。但这是基本的想法。
https://stackoverflow.com/questions/65333189
复制相似问题