我有一个.NET类库,其中包含一个类,该类具有执行一些冗长操作的方法。当客户端调用此方法时,它应该对新线程执行冗长的操作,以避免阻塞调用方。但是一旦方法完成,它应该在主线程上执行一些代码。在WinForms应用程序中,我可以使用System.Windows.Forms.Control.Invoke方法,但这不是我的情况。那么我怎样才能在C#中实现这个目标呢?
答案 0 :(得分:26)
您可以使用System.Windows.Threading.Dispatcher
对象(来自WindowsBase程序集)在特定线程上调用函数。
例如:
public class ClassCreatedBySomeThread
{
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
public void SafelyCallMeFromAnyThread(Action a)
{
dispatcher.Invoke(a);
}
}
答案 1 :(得分:12)
我找到了解决问题的简单方法:
我的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);
}
}
在我的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>
答案 2 :(得分:11)
如果一个线程必须能够执行另一个线程发布到它的某些代码(通常以委托的形式),它必须基本上等待那些指令。你的主线程还在做什么?构建等效的事件循环并不难(基本上你有一个代理的生产者/消费者队列),但是你需要知道你不能只是打断主线程并说“现在就做”。
为什么必须在主线程上执行?
答案 3 :(得分:4)
没有办法明确地让代码在特定的线程上运行(除了用于创建UI控件的线程 - 这是一个例外)但是如果你只是想在线程完成时调用除代码之外的代码,你使用代表 首先声明一个委托,其中包含您要在新线程上运行的方法的签名...
public delegate bool CheckPrimeDelegate(long n);
然后在你的代码中,创建一个委托实例,使用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");
}
}
完成后,它将调用回调函数(OnChkPrimeDone)
如果您明确需要在用于创建COM Active-X对象的线程上运行此回调函数,请检查.Net托管代码包装变量,该变量包含对此对象的引用...如果它具有调用InvokeRequired()的方法,然后在你的回调函数中测试这个方法的布尔返回值 如果它具有InvokeRequired()方法并且它返回true,则active-X对象还将公开“BeginInvoke()”方法。然后,创建另一个委托,填充相同的函数,并调用BeginInvoke 在Active-X对象上,传递给它这个新的委托......然后它将在用于创建Active-X对象的同一个线程上运行
If (MyActiveXObject.InvokeRequired())
MyActiveXObject.BeginInvoke(...);
答案 4 :(得分:2)
线程不能只在另一个线程上执行。你可以得到的最接近的是将一个委托放在一个队列上,以便另一个线程执行,但这假设另一个线程正在合作这个。
在WinForms应用程序中,主循环在每次循环迭代中查找此类排队消息。
如果您只是需要传达工作线程的完成,您可以使用例如旗帜变种。如果主线程应该能够等待作业终止,请使用信号量或条件变量(监视器)。
答案 5 :(得分:2)
这是我的确切场景:我有一个.NET类库公开为COM对象(使用regasm.exe)。 COM对象包含一个运行外部应用程序的方法(使用Process.Start)。 COM对象用于Internet Explorer。所以我的网页运行外部应用程序,我需要找到一种方法将ExitCode传递给网页。
首先,我没有在新线程上启动外部应用程序,只是等待用户关闭应用程序,然后我的函数将ExitCode返回给调用者。但是,当应用程序运行时,IE没有响应。所以我决定在新线程中启动应用程序,但现在我不能再返回ExitCode了。
使用:new ActiveXObject
指令创建COM对象,遗憾的是javascript不支持事件下沉,因此我无法在应用程序退出时触发的C#中编写事件。