簡體   English   中英

我是否需要編組由CreateStreamOnHGlobal返回的IStream,以便跨線程使用?

[英]Do I need to marshal IStream returned by CreateStreamOnHGlobal, for use across threads?

我有一個COM流對象( IStream ),使用CreateStreamOnHGlobal創建。

我想在同一個進程中的不同線程中使用它。 我是否需要編組流對象本身(使用CoMarshalInterface等)? 或者它已經是線程安全的嗎?

EDITED ,讀/寫/搜索與我的代碼中的鎖正確同步。

COM將IStream視為一種特殊類型的接口,可以跨線程安全地使用。 這是必要的,以便可以使用CoMarshalInterThreadInterfaceInStreamIStream線程邊界封送其他接口。

更多信息可以在2003年Dobb博士的文章中找到: Marshaling COM接口

更新:

最初發布的答案並不完全正確。 CreateStreamOnHGlobal返回並通過CoMarshalInterThreadInterfaceInStream間接創建的OLE提供的IStream接口實現可以在同一進程中的線程之間安全地訪問。

文檔分散且難以獲得。 CoMarshalInterThreadInterfaceInStream指出以下內容:

當在接收線程中運行的客戶端嘗試解組指針時,ppStm參數中返回的流保證正常運行。

來自SHCreateMemStream CreateStreamOnHGlobal也有類似的信息:

CreateStreamOnHGlobal創建的流是線程安全的。

對於所有IStream實現,保證通常不適用。 如果您想要安全地使用它,您總是可以使用CoMarshalInterThreadInterfaceInStream跨越線程邊界編組接口,即使不是絕對必要的。 以這種方式編組接口指針永遠不會有害,因為如果不需要編組,COM足夠聰明,不會編組(或重新編組)指針 請記住,這只是一次元帥 - 一次解散 如果要從多個線程解組接口,可以將接口放入全局接口表

根據MSDN 編輯

線程安全。 從Windows 8開始,SHCreateMemStream創建的流是線程安全的。在早期的系統上,該流不是線程安全的。 CreateStreamOnHGlobal創建的流是線程安全的

我有兩個相反的答案,所以我決定驗證它。 看起來@HansPassant是對的,而且@IInspectable是錯的。 至少,在原始 IStream對象的另一個線程上創建代理。

測試用例顯示以下內容:

  • 即使兩個線程都屬於不同的公寓,也可以跨線程使用對IStream的直接引用。 它只是有效。

  • 如果兩個線程都是MTA線程,則來自threadIStreamthread2上被解thread2為完全相同的IUnknown指針,沒有代理。

  • 如果thread1是STA,而thread2是MTA,則有一個代理。 但是在thread上創建的直接引用仍然適用於thread2

注意如何在不同線程的緊密循環內同時對stream1執行讀寫操作。 當然,它沒什么意義,通常有鎖來同步讀/寫。 然而, 它證明了CreateStreamOnHGlobal返回的IStream對象是真正的線程安全的。

我不確定這是否是任何 IStream實現的官方COM要求,正如Dobb博士的文章所暗示的那樣 - 很可能它只是特定於CreateStreamOnHGlobal

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void TestStream()
        {
            // start thread1
            var thread1 = new Thread(() =>
            {
                // create stream1 on thread1
                System.Runtime.InteropServices.ComTypes.IStream stream1;
                CreateStreamOnHGlobal(IntPtr.Zero, true, out stream1);
                IntPtr unkStream1 = Marshal.GetIUnknownForObject(stream1);

                // marshal stream1, to be unmarshalled on thread2
                Guid iid = typeof(System.Runtime.InteropServices.ComTypes.IStream).GUID;
                System.Runtime.InteropServices.ComTypes.IStream marshallerStream;
                CoMarshalInterThreadInterfaceInStream(ref iid, stream1, out marshallerStream);

                // write to stream1
                var buf1 = new byte[] { 1, 2, 3, 4 };
                stream1.Write(buf1, buf1.Length, IntPtr.Zero);

                // start thread2
                var thread2 = new Thread(() =>
                {
                    // read from stream1 (the direct reference) on thread2

                    var buf2 = new byte[buf1.Length];
                    for (var i = 0; i < 10000; i++)
                    {
                        stream1.Seek(0, 0, IntPtr.Zero);
                        stream1.Read(buf2, buf2.Length, IntPtr.Zero);

                        // trule thread safe, this always works!
                        for (var j = 0; j < buf2.Length; j++)
                            Debug.Assert(buf1[j] == buf2[j]);
                    }

                    // Unmarshal and compare IUnknown pointers
                    object stream2;
                    CoGetInterfaceAndReleaseStream(marshallerStream, ref iid, out stream2);
                    IntPtr unkStream2 = Marshal.GetIUnknownForObject(stream2);

                    // Bangs if thread1 is STA, works OK if thread1 is MTA
                    Debug.Assert(unkStream1 == unkStream2);

                    Marshal.Release(unkStream2);
                });

                for (var i = 0; i < 10000; i++)
                {
                    stream1.Seek(0, 0, IntPtr.Zero);
                    stream1.Write(buf1, buf1.Length, IntPtr.Zero);
                }

                thread2.SetApartmentState(ApartmentState.MTA);
                thread2.Start();
                thread2.Join();

                Marshal.Release(unkStream1);
            });

            thread1.SetApartmentState(ApartmentState.STA);
            thread1.Start();
            thread1.Join();
        }

        static void Main(string[] args)
        {
            TestStream();
        }

        [DllImport("ole32.dll", PreserveSig = false)]
        public static extern void CreateStreamOnHGlobal(
            IntPtr hGlobal,
            bool fDeleteOnRelease,
            [Out] out System.Runtime.InteropServices.ComTypes.IStream pStream);

        [DllImport("ole32.dll", PreserveSig = false)]
        public static extern void CoMarshalInterThreadInterfaceInStream(
            [In] ref Guid riid,
            [MarshalAs(UnmanagedType.IUnknown)] object unk,
            out System.Runtime.InteropServices.ComTypes.IStream stream);

        [DllImport("ole32.dll", PreserveSig = false)]
        public static extern void CoGetInterfaceAndReleaseStream(
            [In] System.Runtime.InteropServices.ComTypes.IStream stream,
            [In] ref Guid riid,
            [Out, MarshalAs(UnmanagedType.IUnknown)] out object unk);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM