multithreading System.Windows.Threading.Dispatcher 和 WinForms?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/303116/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
System.Windows.Threading.Dispatcher and WinForms?
提问by David Schmitt
Does a System.Windows.Threading.Dispatcher
work on the UI-thread of a WinForms
application?
是否System.Windows.Threading.Dispatcher
在WinForms
应用程序的 UI 线程上工作?
If yes, why? It is coming from WindowsBase.dll which seems to be a WPF
component.
如果是,为什么?它来自 WindowsBase.dll,它似乎是一个WPF
组件。
If not, how can I invoke work units back onto the UI-thread? I've found Control.BeginInvoke()
, but it seems clumsy to create a control only to reference the originating thread.
如果没有,我如何将工作单元调用回 UI 线程?我发现了Control.BeginInvoke()
,但创建一个仅引用原始线程的控件似乎很笨拙。
回答by Martin Konicek
You can use Dispatcher
even in a WinForms app.
您Dispatcher
甚至可以在 WinForms 应用程序中使用。
If you are sure to be on a UI thread (e.g. in an button.Click handler), Dispatcher.CurrentDispatcher
gives you the UI thread dispatcher that you can later use to dispatch from background threads to the UI thread as usual.
如果您确定在 UI 线程上(例如在 button.Click 处理程序中),则为Dispatcher.CurrentDispatcher
您提供 UI 线程调度程序,您以后可以像往常一样使用它从后台线程调度到 UI 线程。
回答by OJ.
Dispatcher is a WPF component, not a WinForms component.
Dispatcher 是 WPF 组件,而不是 WinForms 组件。
If you want to dispatch work items on the UI thread, then you would have to either use Control.BeginInvoke as you've already found, or react to ResetEvents/WaitObjects across threads.
如果您想在 UI 线程上分派工作项,那么您必须使用您已经找到的 Control.BeginInvoke,或者跨线程对 ResetEvents/WaitObjects 做出反应。
Usually invoking work items on the UI thread is a bad thing unless it's a UI piece of work (ie. updating a control's content or something) in which case the Control.BeginInvoke() would be sufficient.
通常在 UI 线程上调用工作项是一件坏事,除非它是 UI 工作(即更新控件的内容或其他内容),在这种情况下 Control.BeginInvoke() 就足够了。
回答by Gennady Vanin Геннадий Ванин
I provided an example of using System.Windows.Threading.Dispatcher
in Windows Form in my answer to question"Parallel Programming using TPL on WinForms" since the previous answer to your question:
我在回答问题“在 WinForms 上使用 TPL 进行并行编程”的回答中提供了一个System.Windows.Threading.Dispatcher
在 Windows 窗体中使用的示例,因为之前回答了您的问题:
If you are sure to be in UI thread (eg. in an button.Click handler), Dispatcher.CurrentDispatcher gives you the UI thread dispatcher that you can use to dispatch from background thread to UI thread as usual.
如果您确定在 UI 线程中(例如在 button.Click 处理程序中),Dispatcher.CurrentDispatcher 为您提供 UI 线程调度程序,您可以使用它像往常一样从后台线程调度到 UI 线程。
is either misleading or confusing or lacks the concrete usage context:
要么是误导性的,要么是令人困惑的,要么是缺乏具体的使用上下文:
button.Click
handler does not assure to be on UI thread;- if you are not on UI thread, it is still possible to use the disparcher of UI thread of WinForms form
button.Click
处理程序不保证在 UI 线程上;- 如果你不在UI线程上,仍然可以使用WinForms表单的UI线程调度器
One can get dispatcher of WinForm UI thread:
可以获得 WinForm UI 线程的调度器:
Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;
in either button click event handler or anywhere else (in form constructor)
在按钮单击事件处理程序或其他任何地方(在表单构造函数中)
And then use it to execute on UI from other threads, see more details on example below in my answer:
然后使用它从其他线程在 UI 上执行,在我的回答中查看下面示例的更多详细信息:
private void button1_Click(object sender, EventArgs e)
{
Dispatcher dispUI = Dispatcher.CurrentDispatcher;
for (int i = 2; i < 20; i++)
{
int j = i;
var t = Task.Factory.StartNew
(() =>
{
var result = SumRootN(j);
dispUI.BeginInvoke
(new Action
(() => richTextBox1.Text += "root " + j.ToString()
+ " " + result.ToString() + Environment.NewLine
)
, null
);
}
);
}
回答by TonyP
I had similar problem using Oracle dependency class which runs on its own thread within Winforms,
我在使用在 Winforms 中自己的线程上运行的 Oracle 依赖类时遇到了类似的问题,
When OnChange event fired from Oracle Dependency, I wanted to show the changes in DataGridView by simply setting DataSource to eventargs.Details (which is essentially a DataTable), and it throws: System.InvalidOperationException was unhandled by user code Message=Cross-thread operation not valid: Control 'dataGridView1' accessed from a thread other than the thread it was created on.
当从 Oracle Dependency 触发 OnChange 事件时,我想通过简单地将 DataSource 设置为 eventargs.Details(本质上是一个 DataTable)来显示 DataGridView 中的更改,它抛出: System.InvalidOperationException was unhandled by user code Message=Cross-thread operation无效:控件“dataGridView1”从创建它的线程以外的线程访问。
StackOverflow user Brian Peiris ([email protected]) ,collegue of mine showed me this way around:
StackOverflow 用户 Brian Peiris ([email protected]),我的同事向我展示了这种方式:
void dep_OnChange(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Notification received");
int infoSum = int.Parse(arg.Details.Compute("Sum(Info)", "Info is not null").ToString());
InfoSum x = (InfoSum)infoSum;
foreach (DataRow dr in arg.Details.Rows)
{
Console.WriteLine(string.Format("Operation(InfoSum)= {0}", Enum.GetName(typeof(InfoSum), x)));
Console.WriteLine(string.Format("ontable={0} Rowid={1},info={2}", dr.Field<string>("ResourceName"), dr.Field<string>("rowid"), dr.Field<Int32>("info")));
}
// Following will throw cross-thread
// dataGridView1.DataSource = arg.Details;
// instead of line above use the following
dataGridView1.BeginInvoke((Action)(()=>dataGridView1.DataSource = arg.Details));
IsNotified = true;
}
}
回答by Preet Sangha
Use background worker thread as it's UI message pump aware, This MSDN Articlethough mostly about WPF does state that the BWT is UI aware even for windows forms.
使用后台工作线程,因为它可以识别 UI 消息泵,这篇 MSDN 文章虽然主要是关于 WPF 的,但确实声明 BWT 即使对于 Windows 窗体也是 UI 识别的。
回答by rosenfield
Take a look at backgrounderand see if it fits your needs.
看看backgrounder,看看它是否符合您的需求。
回答by George Birbilis
Sometimes a Timer component is useful and easy to setup in WinForms, just set its interval and then enable it, then make sure the first thing you do in its Tick event handler is to disable itself.
有时 Timer 组件在 WinForms 中很有用且易于设置,只需设置其间隔然后启用它,然后确保您在其 Tick 事件处理程序中执行的第一件事就是禁用自身。
I think Timer runs the code in its own thread, so you may still need to do a BeginInvoke (called upon the WinForm object [this]) to run your Action.
我认为 Timer 在它自己的线程中运行代码,因此您可能仍然需要执行一个 BeginInvoke(调用 WinForm 对象 [this])来运行您的 Action。
private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference)
private string imageFilename;
private bool exit;
public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
handler = (s, e) =>
{
webBrowser.DocumentCompleted -= handler; //must do first
this.imageFilename = imageFilename;
this.exit = exit;
timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
timerScreenshot.Enabled = true;
};
webBrowser.DocumentCompleted += handler;
Go(address); //if address == null, will use URL from UI
}
private void timerScreenshot_Tick(object sender, EventArgs e)
{
timerScreenshot.Enabled = false; //must do first
BeginInvoke((Action)(() => //Invoke at UI thread
{ //run in UI thread
BringToFront();
Bitmap bitmap = webBrowser.GetScreenshot();
if (imageFilename == null)
imageFilename = bitmap.ShowSaveFileDialog();
if (imageFilename != null)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
bitmap.Save(imageFilename);
}
bitmap.Dispose(); //release bitmap resources
if (exit)
Close(); //this should close the app, since this is the main form
}), null);
}
you can see the above in action at WebCapture tool (http://gallery.clipflair.net/WebCapture, source code at: http://ClipFlair.codeplex.com, see Tools/WebCapture folder) that grabs screenshots from websites. BTW, if you want to call the executable from command-line make sure you go to Properties of the project and at Security tab turn-off ClickOnce security (else it can't access command-line)
您可以在 WebCapture 工具(http://gallery.clipflair.net/WebCapture,源代码在:http://ClipFlair.codeplex.com,请参阅 Tools/WebCapture 文件夹)中看到上述操作,该工具从网站上抓取屏幕截图。顺便说一句,如果您想从命令行调用可执行文件,请确保转到项目的属性并在安全选项卡关闭 ClickOnce 安全性(否则它无法访问命令行)