C# 如何在 .NET 中调用父线程上的函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/269988/
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
How to invoke a function on parent thread in .NET?
提问by Darin Dimitrov
I have a .NET class library containing a class with a method that performs some lengthy operation. When a client calls this method it should perform the lengthy operation on a new thread in order to avoid blocking the caller. But once the method finishes it should execute some code on the main thread. In a WinForms application I could have used the System.Windows.Forms.Control.Invoke method but this is not my case. So how can I achieve this in C#?
我有一个 .NET 类库,其中包含一个带有执行一些冗长操作的方法的类。当客户端调用此方法时,它应该在新线程上执行冗长的操作以避免阻塞调用者。但是一旦方法完成,它应该在主线程上执行一些代码。在 WinForms 应用程序中,我可以使用 System.Windows.Forms.Control.Invoke 方法,但这不是我的情况。那么我怎样才能在 C# 中实现这一点呢?
采纳答案by Darin Dimitrov
I found a simple solution to the problem :
我找到了一个简单的问题解决方案:
My COM object is declared like this:
我的 COM 对象声明如下:
public class Runner
{
public void Run(string executable, object processExitHandler)
{
ThreadPool.QueueUserWorkItem(state =>
{
var p = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = executable
}
};
p.Start();
while (!p.HasExited)
{
Thread.Sleep(100);
}
state
.GetType()
.InvokeMember(
"call",
BindingFlags.InvokeMethod,
null,
state,
new object[] { null, p.ExitCode }
);
}, processExitHandler);
}
}
And in my HTML page I use it like this:
在我的 HTML 页面中,我像这样使用它:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><title>ActiveXRunner</title>
<script type="text/javascript">
function runNotepad() {
var ax = new ActiveXObject('ActiveXRunner.Runner');
ax.Run('c:\windows\notepad.exe', h);
}
function h(exitCode) {
alert('exitCode = ' + exitCode);
}
</script>
</head>
<body>
<a href="#" onclick="runNotepad();">Run notepad and show exit code when finished</a>
</body>
</html>
回答by Jon Skeet
If a thread has to be able to execute some bit of code (usually in the form of a delegate) posted to it by another thread, it will have to basically be waiting for those instructions. What else is your main thread doing? It's not that hard to build an equivalent of the event loop (basically you'd have a producer/consumer queue of delegates) but you need to be aware that you can't just interrupt the main thread and say "do this now".
如果一个线程必须能够执行由另一个线程发布给它的一些代码(通常以委托的形式),那么它基本上必须等待这些指令。你的主线程还在做什么?构建一个事件循环的等价物并不难(基本上你会有一个生产者/消费者代表队列),但你需要意识到你不能只是中断主线程并说“现在做这个”。
Why does this have to execute on the main thread?
为什么这必须在主线程上执行?
回答by Bjarke Ebert
A thread cannot just execute stuff on another thread. The closest you can get is to put a delegate on a queue for the other thread to execute, but that assumes that the other thread is cooperating about this.
一个线程不能只在另一个线程上执行内容。您可以获得的最接近的是将委托放在队列中供另一个线程执行,但前提是另一个线程正在对此进行合作。
In a WinForms application, the main loop looks for such queued messages on each loop iteration.
在 WinForms 应用程序中,主循环在每次循环迭代中查找此类排队消息。
If you just need to communicate the finishing of the worker thread, you can use e.g. a flag variabe. If the main thread should be able to wait for job termination, use a semaphore or condition variable (monitor).
如果您只需要传达工作线程的完成情况,您可以使用例如标志变量。如果主线程应该能够等待作业终止,请使用信号量或条件变量(监视器)。
回答by Darin Dimitrov
Here's my exact scenario: I have a .NET class library exposed as a COM object (using regasm.exe). The COM object contains a method that runs an external application (using Process.Start). The COM object is used in Internet Explorer. So my web page runs the external application and I need to find a way to pass the ExitCode to the web page.
这是我的确切场景:我有一个公开为 COM 对象的 .NET 类库(使用 regasm.exe)。COM 对象包含一个运行外部应用程序的方法(使用 Process.Start)。COM 对象用于 Internet Explorer。所以我的网页运行外部应用程序,我需要找到一种方法将 ExitCode 传递给网页。
At first I didn't start the external application on a new thread and just waited for the user to close the application and then my function returned the ExitCode to the caller. But while the application was running IE was not responsive. So I decided to start the application in a new thread but now I can no longer return the ExitCode.
起初我没有在新线程上启动外部应用程序,只是等待用户关闭应用程序,然后我的函数将 ExitCode 返回给调用者。但是当应用程序运行时 IE 没有响应。所以我决定在一个新线程中启动应用程序,但现在我不能再返回 ExitCode。
The COM object is created with: new ActiveXObject
instruction and unfortunately javascript doesn't support event sinking so I can't write an event in C# that's triggered when the application exits.
COM 对象是用:new ActiveXObject
指令创建的,不幸的是 javascript 不支持事件接收,所以我不能用 C# 编写一个在应用程序退出时触发的事件。
回答by Charles Bretana
There's no way to explicitly make code run on a specific thread, (except for the thread that was used to create UI Controls - this is an exception) but if you simply want to call code other than the code when a thread completes, You use delegates.
First declare a delegate with the signature of the method you want to run on the new thread...
没有办法显式地让代码在特定线程上运行(除了用于创建 UI 控件的线程 - 这是一个例外)但是如果您只想在线程完成时调用代码以外的代码,您可以使用代表。
首先声明一个带有要在新线程上运行的方法的签名的委托...
public delegate bool CheckPrimeDelegate(long n);
Then in your code, create an instance of the delegate, call it using BeginInvoke (passing any parameters it needs) and PASS a callback function delegate (OnChkPrimeDone)
然后在您的代码中,创建一个委托实例,使用 BeginInvoke(传递它需要的任何参数)调用它并传递一个回调函数委托 (OnChkPrimeDone)
class MyClassApp
{
static void Main()
{
CheckPrimeDelegate ckPrimDel = new CheckPrimeDelegate(Prime.Check);
// Initiate the operation
ckPrimDel.BeginInvoke(4501232117, new AsyncCallback(OnChkPrimeDone), null);
// go do something else . . . .
}
static void OnChkPrimeDone( IAsyncResult iAr)
{
AsyncResult ar = iAr as AsynchResult;
CheckPrimeDelegate ckPrimDel = ar.AsyncDelegate as CheckPrimeDelegate;
bool isPrime = ckPrimDel.EndInvoke(ar);
Console.WriteLine(" Number is " + (isPrime? "prime ": "not prime");
}
}
When it is done, it will call the callback function (OnChkPrimeDone)
完成后,它会调用回调函数(OnChkPrimeDone)
If you explicitly need to run this callback function on the thread that was used to create the COM Active-X object, then check the .Net Managed Code wrapper variable that holds a reference to this object... If it has a method called InvokeRequired(), then, in your callback function, test the boolean return value of this method.
If it has InvokeRequired() method and it returns true, then the active-X object will also expose a "BeginInvoke()" Method. Then, Create ANOTHER Delegate, populated with the same function, and call BeginInvoke
on the Active-X Object, passing it this new delegate... It will then run on the same thread as was used to create teh Active-X Object
如果您明确需要在用于创建 COM Active-X 对象的线程上运行此回调函数,请检查保存对此对象引用的 .Net 托管代码包装器变量...如果它有一个名为 InvokeRequired 的方法(),然后,在您的回调函数中,测试此方法的布尔返回值。
如果它有 InvokeRequired() 方法并且它返回 true,那么 active-X 对象也将公开一个“BeginInvoke()”方法。然后,创建另一个委托,填充相同的函数,并在 Active-X 对象上调用 BeginInvoke,将这个新委托传递给它......然后它将在与创建 Active-X 对象相同的线程上运行
If (MyActiveXObject.InvokeRequired())
MyActiveXObject.BeginInvoke(...);
回答by cdiggins
You can invoke a function on a specific thread by using a System.Windows.Threading.Dispatcher
object (from the WindowsBase assembly).
您可以使用System.Windows.Threading.Dispatcher
对象(来自 WindowsBase 程序集)调用特定线程上的函数。
For example:
例如:
public class ClassCreatedBySomeThread
{
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
public void SafelyCallMeFromAnyThread(Action a)
{
dispatcher.Invoke(a);
}
}