- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
TL; DR:StaTaskScheduler
运行的任务内的死锁。长版:
我正在使用Parallel Team的 StaTaskScheduler
中的ParallelExtensionsExtras托管第三方提供的一些旧版STA COM对象。 StaTaskScheduler
实现细节的描述如下:
The good news is that TPL’s implementation is able to run on either MTA or STA threads, and takes into account relevant differences around underlying APIs like WaitHandle.WaitAll (which only supports MTA threads when the method is provided multiple wait handles).
CoWaitForMultipleHandles
之类的消息,以避免在STA线程上调用时出现死锁情况。
var result = await Task.Factory.StartNew(() =>
{
// in-proc object A
var a = new A();
// out-of-proc object B
var b = new B();
// A calls B and B calls back A during the Method call
return a.Method(b);
}, CancellationToken.None, TaskCreationOptions.None, staTaskScheduler);
a.Method(b)
永不返回。据我所知,发生这种情况是因为
BlockingCollection<Task>
内部某处的阻塞等待不会泵送消息,因此我对引用语句的假设可能是错误的。
TaskScheduler.FromCurrentSynchronizationContext()
提供
staTaskScheduler
而不是
Task.Factory.StartNew
)。
CoWaitForMultipleHandles
泵送消息并将其安装在由
StaTaskScheduler
启动的每个STA线程上?
BlockingCollection
的底层实现是否将调用我的
SynchronizationContext.Wait
方法?我可以使用
SynchronizationContext.WaitHelper
来实现
SynchronizationContext.Wait
吗?
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleTestApp
{
class Program
{
// start and run an STA thread
static void RunStaThread(bool pump)
{
// test a blocking wait with BlockingCollection.Take
var tasks = new BlockingCollection<Task>();
var thread = new Thread(() =>
{
// Create a simple Win32 window
var hwndStatic = NativeMethods.CreateWindowEx(0, "Static", String.Empty, NativeMethods.WS_POPUP,
0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
// subclass it with a custom WndProc
IntPtr prevWndProc = IntPtr.Zero;
var newWndProc = new NativeMethods.WndProc((hwnd, msg, wParam, lParam) =>
{
if (msg == NativeMethods.WM_TEST)
Console.WriteLine("WM_TEST processed");
return NativeMethods.CallWindowProc(prevWndProc, hwnd, msg, wParam, lParam);
});
prevWndProc = NativeMethods.SetWindowLong(hwndStatic, NativeMethods.GWL_WNDPROC, newWndProc);
if (prevWndProc == IntPtr.Zero)
throw new ApplicationException();
// post a test WM_TEST message to it
NativeMethods.PostMessage(hwndStatic, NativeMethods.WM_TEST, IntPtr.Zero, IntPtr.Zero);
// BlockingCollection blocks without pumping, NativeMethods.WM_TEST never arrives
try { var task = tasks.Take(); }
catch (Exception e) { Console.WriteLine(e.Message); }
if (pump)
{
// NativeMethods.WM_TEST will arrive, because Win32 MessageBox pumps
Console.WriteLine("Now start pumping...");
NativeMethods.MessageBox(IntPtr.Zero, "Pumping messages, press OK to stop...", String.Empty, 0);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Thread.Sleep(2000);
// this causes the STA thread to end
tasks.CompleteAdding();
thread.Join();
}
static void Main(string[] args)
{
Console.WriteLine("Testing without pumping...");
RunStaThread(false);
Console.WriteLine("\nTest with pumping...");
RunStaThread(true);
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}
}
// Interop
static class NativeMethods
{
[DllImport("user32")]
public static extern IntPtr SetWindowLong(IntPtr hwnd, int nIndex, WndProc newProc);
[DllImport("user32")]
public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hwnd, int msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern IntPtr CreateWindowEx(int dwExStyle, string lpClassName, string lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern int MessageBox(IntPtr hwnd, string text, String caption, int options);
public delegate IntPtr WndProc(IntPtr hwnd, int msg, int wParam, int lParam);
public const int GWL_WNDPROC = -4;
public const int WS_POPUP = unchecked((int)0x80000000);
public const int WM_USER = 0x0400;
public const int WM_TEST = WM_USER + 1;
}
}
最佳答案
我对您的问题的理解:您仅使用 StaTaskScheduler
来为传统的COM对象组织经典的COM STA公寓。您是而不是,它在StaTaskScheduler
的STA线程上运行WinForms或WPF核心消息循环。也就是说,您在该线程中没有使用Application.Run
,Application.DoEvents
或Dispatcher.PushFrame
之类的东西。如果这是一个错误的假设,请纠正我。
就其本身而言,StaTaskScheduler
不会在其创建的STA线程上安装任何同步上下文。因此,您依赖CLR为您发送消息。我仅在克里斯·布鲁姆(Chris Brumme)的Apartments and Pumping in the CLR中发现了一个隐式确认,即CLR在STA线程上运行:
I keep saying that managed blocking will perform “some pumping” when called on an STA thread. Wouldn’t it be great to know exactly what will get pumped? Unfortunately, pumping is a black art which is beyond mortal comprehension. On Win2000 and up, we simply delegate to OLE32’s CoWaitForMultipleHandles service.
CoWaitForMultipleHandles
。此外,
COWAIT_DISPATCH_WINDOW_MESSAGES
标志
mention this的MSDN文档:
... in STA is only a small set of special-cased messages dispatched.
WM_TEST
从示例代码中抽取
CoWaitForMultipleHandles
,我们在对您的问题的评论中进行了讨论。我的理解是,前面提到的一小组特殊情况的消息
实际上将限制为某些COM编码特定的消息,并且不包括任何常规的通用消息,例如
WM_TEST
。
... Should I implemented a custom synchronization context, which would explicitly pump messages with CoWaitForMultipleHandles, and install it on each STA thread started by StaTaskScheduler?
SynchronizationContext.Wait
确实是正确的解决方案。
CoWaitForMultipleHandles
,而
使用 MsgWaitForMultipleObjectsEx
而不是。如果
MsgWaitForMultipleObjectsEx
指示队列中有待处理的消息,则应手动使用
PeekMessage(PM_REMOVE)
和
DispatchMessage
泵送它。然后,您应该继续等待所有在相同
SynchronizationContext.Wait
调用中的句柄。
MsgWaitForMultipleObjectsEx
和 MsgWaitForMultipleObjects
之间有一个细微但重要的区别。如果队列中已经看到一条消息(例如
PeekMessage(PM_NOREMOVE)
或
GetQueueStatus
),但后者未删除,则后者不会返回并保持阻塞状态。这对泵送不利,因为您的COM对象可能正在使用
PeekMessage
之类的东西来检查消息队列。稍后可能会导致
MsgWaitForMultipleObjects
在不期望的时候阻塞。
MsgWaitForMultipleObjectsEx
标志的
MWMO_INPUTAVAILABLE
没有这种缺点,在这种情况下会返回。
StaTaskScheduler
(
available here as ThreadAffinityTaskScheduler
)来尝试解决
different problem:为后续的
await
延续保留一个具有线程相似性的线程池。如果您在多个
awaits
中使用STA COM对象,则线程亲和性为
和。原始
StaTaskScheduler
仅在其池限制为1个线程时才显示此行为。
WM_TEST
案例实验。最初,我在STA线程上安装了标准
SynchronizationContext
类的实例。没有收到
WM_TEST
消息,这是预期的。
SynchronizationContext.Wait
,将其转发给
SynchronizationContext.WaitHelper
。它确实被调用了,但是仍然没有启动。
// the core loop
var msg = new NativeMethods.MSG();
while (true)
{
// MsgWaitForMultipleObjectsEx with MWMO_INPUTAVAILABLE returns,
// even if there's a message already seen but not removed in the message queue
nativeResult = NativeMethods.MsgWaitForMultipleObjectsEx(
count, waitHandles,
(uint)remainingTimeout,
QS_MASK,
NativeMethods.MWMO_INPUTAVAILABLE);
if (IsNativeWaitSuccessful(count, nativeResult, out managedResult) || WaitHandle.WaitTimeout == managedResult)
return managedResult;
// there is a message, pump and dispatch it
if (NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, NativeMethods.PM_REMOVE))
{
NativeMethods.TranslateMessage(ref msg);
NativeMethods.DispatchMessage(ref msg);
}
if (hasTimedOut())
return WaitHandle.WaitTimeout;
}
WM_TEST
被抽出。 以下是测试的适应版本:
public static async Task RunAsync()
{
using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(staThread: true, pumpMessages: true))
{
Console.WriteLine("Initial thread #" + Thread.CurrentThread.ManagedThreadId);
await staThread.Run(async () =>
{
Console.WriteLine("On STA thread #" + Thread.CurrentThread.ManagedThreadId);
// create a simple Win32 window
IntPtr hwnd = CreateTestWindow();
// Post some WM_TEST messages
Console.WriteLine("Post some WM_TEST messages...");
NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(1), IntPtr.Zero);
NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(2), IntPtr.Zero);
NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(3), IntPtr.Zero);
Console.WriteLine("Press Enter to continue...");
await ReadLineAsync();
Console.WriteLine("After await, thread #" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Pending messages in the queue: " + (NativeMethods.GetQueueStatus(0x1FF) >> 16 != 0));
Console.WriteLine("Exiting STA thread #" + Thread.CurrentThread.ManagedThreadId);
}, CancellationToken.None);
}
Console.WriteLine("Current thread #" + Thread.CurrentThread.ManagedThreadId);
}
await
之后停留在线程#10上)和消息泵送。完整的源代码包含可重用的部分(
ThreadAffinityTaskScheduler
和
ThreadWithAffinityContext
),并且可以使用
here as self-contained console app。它尚未经过全面测试,因此使用时需您自担风险。
关于c# - StaTaskScheduler和STA线程消息泵送,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21211998/
我必须将.NET应用程序作为插件包含到另一个.NET应用程序中。插件界面要求我从模板表单继承。然后,在加载插件时,将表单附加到MDI中。 到目前为止,一切正常,但是每当我注册拖放事件时,都为组合框设置
我有一个必须作为 STA 运行的函数,我想将它的异常传播到调用线程。在这里: public void ExceptionBePropagatedThroughHere() { Thread thr
我正在将一个线程初始化为静态线程,如下所示 Thread GenerateKeywords; private void btnStart_Click(object sender, EventArgs
我们正在 AZURE 上开发“ Multi-Tenancy 应用程序” (MTA)。此外,我们为通过 REST API 端点利用 MTA 数据的客户开发“单租户应用程序”(STA),即可以在任何地方托
我从 GUI 线程调用这个函数: let updateImageLoop (pprocess : PlotProcess) (target : IUpdatableImageView) = a
我有一个类,当某些操作的状态发生变化时,它应该向用户显示一些消息,如下所示: public static class AutoUpdater { public static a
我有一个 BackgroundWorker 调用函数在 BackgroundWorker _DoWork 做一个长进程,当函数发生错误时我会提示一个自定义的消息框: WPFMessageBoxRes
我一直在尝试一些最终可能变成不可能的事情。自从我进入 COM 领域以来已经有很长一段时间了。 考虑两个应用程序和一个 COM STA DLL。第一个应用程序将 COM STA DLL 作为插件加载,此
这个问题在这里已经有了答案: How to create a task (TPL) running a STA thread? (2 个答案) 关闭 8 年前。 我正在 VS2010 中开发 VST
我正在尝试创建 COM 组件,它会经常被 excel 应用程序调用(excel 将在其初始化时加载 COM)并且另一个进程(比如 procA)也会(以高频率)发送 windows 消息到这个组件。目前
所以,关注 this ,我决定在专用 STA 线程上显式实例化一个 COM 对象。实验表明 COM 对象需要一个消息泵,这是我通过调用 Application.Run() 创建的: private M
我有这个遗留的第三方 COM DLL。我将其注册到注册表并将 RCW 添加到我的 .NET/C# 控制台应用程序。注册表将 COM 的线程模型 显示为单元。我的应用程序的全部目的是使用多线程并发提交多
我有一个小的 c# 类需要公开为 COM。将使用 COM 对象的工具要求我支持 STA。我需要对 COM 对象做些什么特别的事情吗? (我知道 STAThread 属性,但是(除非我错了)它用于指示
在我的 WPF 应用程序中,我进行了一些异步通信(与服务器)。在回调函数中,我最终根据服务器的结果创建了 InkPresenter 对象。这要求正在运行的线程是 STA,而目前显然不是。因此我得到以下
如果我在 .NET 中有一个 STA 线程,并且我在该线程中创建了一个 STA COM 对象,然后该线程结束——这是否会杀死该对象的实例? 我的理解是否正确,STA COM 对象可以被多个线程访问并且
我正在 PowerShell 中处理 GUI,当单击某些组合框时我会抛出错误。 抛出错误后,我可以放下组合框列表并查看其内容,但是如果我切换到同一个 datagridview 上的另一个组合框,我会在
在过去的几个月中,我一直在尝试使用Powershell(2.0),并且很乐意使用它来对一些正在使用的进程进行现代化和标准化-大多是基于DOS的进程。由于工作的性质,一次执行大约100次相同脚本的执行。
谁能解释一下 LDA、ROL、STA 给出的每条指令的确切结果是什么? C=1 (carry flag is 1) LDA A #$08 0008 | 01 ROL A 0009 | 02
我是一名硕士生,我正在通过 STATISTICA 程序学习统计学类(class)。我对 R 比较熟悉,并想坚持下去。所以我打算在 R 中做提供的练习。但是要使用的数据格式为 *.sta... 有没有办
我试图让 Watin 在我的 SSIS 脚本任务中工作,通过在新线程中打开 IE 来执行一些自动化操作,做一些事情,找到最终值并基本上在父线程中返回/设置该值。 所以我现在有这个: public pa
我是一名优秀的程序员,十分优秀!