标签: apartments

你能解释一下STA和MTA吗?

你能用自己的话解释STA和MTA吗?

什么是公寓线程,它们只与COM有关吗?如果是这样,为什么?

.net com multithreading apartments

385
推荐指数
7
解决办法
13万
查看次数

单螺纹公寓vs多线程公寓

可能重复:
你能解释一下STA和MTA吗?

所有ThreadPool线程都在多线程单元中.

- 根据MSDN

那是什么意思?我真的很关心多线程和单线程公寓模型之间的区别.或者公寓模型是什么意思?我已经阅读了它的MSDN,这对我来说并没有多大意义.我想我可能有一个想法,但我认为这里有人可以用简单的英语解释它.

谢谢,安东尼D.

更新1

发现这 可以解释STA和MTA吗?

任何人都可以更具描述性吗?

更新2

我也在寻找一个关于如何应用于线程池的答案,以及因此而需要注意的事项.

.net c# multithreading apartments

39
推荐指数
4
解决办法
5万
查看次数

传统的VB6 COM + DLL调用本机Win32 DLL - 与STA的线程问题?

看看像MT一样的第一眼看到的东西,但我试图详细了解COM +使用的STA模型.

实际上,我有一个用VB6编写的遗留COM +组件,它调用用C++编写的本机(即非COM)Win32 DLL.

有一些间歇性(并且不可能在测试中重现)问题,我添加了一些调试代码来找出发生了什么,并发现当问题发生时,我在文件中交错了日志消息 - 所以它暗示了DLL被两个线程同时调用.

现在,日志记录转到基于_getpid()和GetCurrentThreadId()的每线程文件,所以看起来当调用C++ DLL中的代码时,它会在同一个线程上同时被调用两次.我对STA的理解表明,可能就是这种情况,因为COM将对象的各个实例编组到一个线程上,并随意恢复执行.

不幸的是,我不确定从哪里开始.我正在读我应该在DllMain()中调用CoInitialiseEx()告诉COM这是一个STA DLL,但其他地方说这只对COM DLL有效,并且在本机DLL中不会有任何影响.唯一的另一个选择是将DLL的一部分包装为关键部分以序列化访问(获取下巴上的任何性能命中).

我可以尝试重做DLL,但是没有共享状态或全局变量 - 一切都在局部变量中所以理论上每个调用应该得到自己的堆栈,但我想知道STA模型是否基本上对此有一些奇怪的影响并在与另一个调用相同的入口点重新进入已加载的DLL.不幸的是,我不知道如何证明或测试这个理论.

问题基本上是:

  1. 当STA COM +组件调用本机DLL时,STA模型中没有任何内容可以防止活动"线程"被挂起并且控制在DLL调用过程中被传递给另一个"线程"?
  2. CoInitialiseEx()是解决这个问题的正确方法吗?
  3. 如果(1)或(2)都不是"好的"假设,那么会发生什么?

vb6 com multithreading apartments

7
推荐指数
1
解决办法
1833
查看次数

关于公寓穿线模型

我对公寓有几个问题:

  1. 此模型仅用于COM目的吗?
  2. 你能指出关于他们的好资源吗?
  3. 从线程安全的角度来看,组件支持STA或MTA模型意味着什么?

谢谢.

com multithreading apartments

7
推荐指数
1
解决办法
2214
查看次数

CoInitializeEx在COM对象内部调用时返回S_OK

前段时间,我不得不修改一个旧的COM DLL(Visual C++ 2010,ATL),将它从"Apartment"线程模型迁移到"Both",即现在可以从STA和MTA线程调用它而无需序列化调用(当然,我不得不为共享数据添加内部同步.当从.NET应用程序通过Interop调用我的DLL时,这会在将COM事件(连接点)转换为.NET事件时导致问题(即使在.NET应用程序中我也必须支持STA和MTA).为了解决这些问题,我改变了触发事件的方式.

1)如果在STA上下文中调用DLL,它就像以前一样工作,即它创建一个不可见的窗口,然后,当必须引发事件时,它调用PostMessage到该窗口,然后主STA线程调用实际的事件 - 触发代码,即CProxy_IMyEventFiringInterface成员函数(CProxy_IMyEventFiringInterface派生自IConnectionPointImpl).

2)如果在MTA上下文中调用DLL,我没有主COM线程,我不能做PostMessage,所以我使用我创建的自定义线程,让该线程调用IConnectionPointImpl函数.

但AFAIK没有Windows API可以检测调用线程是STA还是MTA.许多网站都建议像这样使用CoInitializeEx:

HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch(hr)
{
    case S_FALSE:  // we are in a Multi-Threaded Apartment (MTA)
    CoUninitialize(); // each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize
    break;
    case RPC_E_CHANGED_MODE:  // we are in a Single-Threaded Apartment (STA)
    break;
    default:  // IMPOSSIBLE!!!!
}
Run Code Online (Sandbox Code Playgroud)

我决定将此调用放在CMyComComponent :: FinalConstruct中的CoInitializeEx中.一切都很好......直到今天.在客户场景中,我从我的跟踪工具中看到,对于某个.NET EXE应用程序(我没有源代码),上面的代码最终会出现在默认分支中,因为CoInitializeEx返回了S_OK.这怎么可能?Microsoft文档说S_OK意味着"COM库已在此线程上成功初始化",但我是INSIDE COM对象,COM库必须已经初始化!顺便说一句,默认分支不会关闭应用程序,但是,由于返回了S_OK,它调用了CoUninitialize(每次成功调用CoInitialize或CoInitializeEx,包括任何返回S_FALSE的调用,必须通过对CoUninitialize的相应调用来平衡)然后DLL继续假定STA(后见之明的错误移动).但它不是STA:事实上,后来的Pos​​tMessage返回FALSE.

我可以简单地更改代码以使用MTA作为默认值,如果CoInitializeEx(NULL,COINIT_MULTITHREADED)返回S_OK,我应该从一开始就做到了.但我也希望确保这是正确的做法,以避免将来出现进一步的问题.非常感谢Demetrio

c++ com atl apartments com-interop

6
推荐指数
1
解决办法
3744
查看次数

C# 在线程之间编组 COM 对象

我对 C# marshal 的 COM 对象是否在线程之间感到非常困惑。为此,我有一个以任务并行方式加载一组文件的应用程序。我正在使用StaTaskScehduler使用 COM 对象加载文件。加载 COM 对象后,我将该对象存储在中央列表中。

然后我稍后尝试对这些数据执行一些处理,再次使用 STATaskScheduler。但是在这一点上我遇到了一个问题。我收到如下异常:

An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in MadCat.exe

Additional information: COM object that has been separated from its underlying RCW cannot be used
Run Code Online (Sandbox Code Playgroud)

现在我的理解是我收到此错误是因为该对象尚未编组到新线程中。我认为这是 C# 为您做的事情?

如何在一个线程中创建一个单元线程 COM 对象,然后从另一个线程使用它?

我在这里吠错树了吗?我什至不应该在我的线程中使用 Sta 公寓吗?我可以保证对象永远不会同时从多个线程访问。任何想法都非常感谢。

编辑:COM 对象定义如下:

[
    coclass,
    threading( apartment ),
    vi_progid( [Namespace.Class] ),
    progid( [Namespace.Class].6 ),
    version( 6.0 ),
    uuid( GUID_C[Class] ),
    helpstring( [Class]" Class" )
]
Run Code Online (Sandbox Code Playgroud)

所以根据我的理解,这是一个公寓线程对象,对吗?我刚刚尝试使用未设置公寓状态的修改后的任务调度程序(默认情况下为 MTA?)。当我在一个线程中创建它并从另一个线程使用它时,这个对象似乎确实有效。这是安全的还是会以其他方式回来咬我?

COM 的线程模型总是让我很困惑:/

c# com multithreading apartments

5
推荐指数
1
解决办法
1975
查看次数

在WinForms线程上使用CoInitializeEx

我正在为DSLR相机制作SDK,其中包含以下说明:

开发Windows应用程序的注意事项创建在Windows下运行的应用程序时,每个线程都需要进行COM初始化,以便从主线程以外的线程访问摄像机.要创建用户线程并从该线程访问摄像机,请确保在线程的开头执行CoInitializeEx(NULL,COINIT_APARTMENTTHREADED),最后执行CoUnInitialize().示例代码如下所示.从另一个线程控制EdsVolumeRef或EdsDirectoryItemRef对象时,这是相同的,而不仅仅是EdsCameraRef.

void TakePicture(EdsCameraRef camera)
{
    // Executed by another thread
    HANDLE hThread = (HANDLE)_beginthread(threadProc, 0, camera);
    // Block until finished
    ::WaitForSingleObject( hThread, INFINITE );
}

void threadProc(void* lParam)
{
    EdsCameraRef camera = (EdsCameraRef)lParam;
    CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
    EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    CoUninitialize();
    _endthread();
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序是一个C#WinForms应用程序,通常,我使用托管线程类和Control.Invoke函数来避免跨线程问题.

由于我没有使用C#中的示例源代码来使用SDK,我的问题是,CoInitializeEx在标记有[STAThread]属性的应用程序中使用它是否有用和/或必要?

我没有遇到过一个场景,我需要让我的应用程序为线程创建一个新的公寓,这样一些洞察力将有助于更好地理解线程模型.

更新:在阅读了更多关于公寓和COM之后,它开始有所了解.现在我想知道.NET托管线程类默认是什么,我们可以以托管的方式为每个线程指定一个没有P/Invoke的公寓模型吗?

.net c# com multithreading apartments

5
推荐指数
1
解决办法
2540
查看次数

对于具有ThreadingModel Both的对象,是否需要COM编组?

这是由另一个问题引发的.

具体地讲,我有一个在过程COM类中,在所定义的CLSID注册表为具有ThreadingModelBoth.

我们的进程激活了这个对象CoCreateInstance(不是 CoCreateInstanceEx,如果这对于进程内DLL服务器来说很重要)

Both文档中列出的规则的线程模型和给定规则:

Threading model of server | Apartment server is run in
------------------------------------------------------
Both                      | Same apartment as client
Run Code Online (Sandbox Code Playgroud)

鉴于汉斯在另一个答案中所写的内容:

...当需要在不同的线程上进行客户端调用时,就会发生编组....当comClass元素中指定的ThreadingModel需要它时,可能会发生.换句话说,当COM对象在一个线程上创建但在另一个线程上调用时,服务器不是线程安全的.

我的初步结论是,这样的对象永远不需要对其接口的调用进行隐式编组,因为该对象将始终与其客户端位于同一个公寓中.

这是正确的,即使客户端进程作为STA运行?

c++ com apartments marshalling sta

4
推荐指数
1
解决办法
587
查看次数

在线程中执行Webbrowser控件的屏幕视图

我正在使用中所示的技术

WebBrowser控件在一个新线程中

尝试获取网页的屏幕抓取我已经能够获得以下代码,以便在WebBrowser控件放置在a上时成功工作WinForm.但是,当在线程内运行时,它会通过提供桌面的任意图像而失败.

Thread browserThread = new Thread(() =>
{
    WebBrowser br = new WebBrowser();
    br.DocumentCompleted += webBrowser1_DocumentCompleted;
    br.ProgressChanged += webBrowser1_ProgressChanged;
    br.ScriptErrorsSuppressed = true;
    br.Navigate(url);
    Application.Run();
});
browserThread.SetApartmentState(ApartmentState.STA);
browserThread.Start();

private Image TakeSnapShot(WebBrowser browser)
{
    int width;
    int height;

    width = browser.ClientRectangle.Width;
    height = browser.ClientRectangle.Height;

    Bitmap image = new Bitmap(width, height);

    using (Graphics graphics = Graphics.FromImage(image))
    {
        Point p = new Point(0, 0);
        Point upperLeftSource = browser.PointToScreen(p);
        Point upperLeftDestination = new Point(0, 0);

        Size blockRegionSize = browser.ClientRectangle.Size; …
Run Code Online (Sandbox Code Playgroud)

c# multithreading screen-scraping apartments webbrowser-control

3
推荐指数
1
解决办法
3451
查看次数

如何使控制台应用程序的主线程成为MTA?

使用Delphi,如何创建一个主线程初始化为的(Windows)控制台应用程序COINIT_MULTITHREADED

如果我CoInitializeEx(nil, COINIT_MULTITHREADED)在第一条语句中调用,则会得到一个HRESULT 0x80010106(设置后无法更改线程模式),因此显然某些先前正在运行的代码已被调用CoInitialize/Ex

如何获得主线程COINIT_MULTITHREADED

delphi com apartments apartment-state

2
推荐指数
1
解决办法
76
查看次数