.NET Interop调用仅限于单线程?

时间:2013-07-08 07:28:46

标签: c# .net multithreading interop

我有以下使用新的.NET 4.5多线程功能的代码。 Action2是通过Interop调用Windows API库MLang。

    BlockingCollection<int> _blockingCollection= new BlockingCollection<int>(); 


    [Test]
    public void Do2TasksWithThreading()
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        var tasks = new List<Task>();
        for (int i = 0 ; i < Environment.ProcessorCount; i++)
        {
            tasks.Add((Task.Factory.StartNew(() => DoAction2UsingBlockingCollection(i))));
        }

        for (int i = 1; i < 11; i++)
        {
            DoAction1(i);

            _blockingCollection.Add(i);
        }

        _blockingCollection.CompleteAdding();

        Task.WaitAll(tasks.ToArray());

        stopwatch.Stop();

        Console.WriteLine("Total time: " + stopwatch.ElapsedMilliseconds + "ms");
    }

    private void DoAction2UsingBlockingCollection(int taskIndex)
    {
        WriteToConsole("Started wait for Action2 Task: " + taskIndex);

        int index;
        while (_blockingCollection.Count > 0 || !_blockingCollection.IsAddingCompleted)
        {
            if (_blockingCollection.TryTake(out index, 10))
                DoAction2(index);
        }

        WriteToConsole("Ended wait for Action2 Task: " + taskIndex);
    }



    private void DoAction2()
    {
                    ... Load File bytes

        //Call to MLang through interop
        Encoding[] detected = EncodingTool.DetectInputCodepages(bytes[], 1);

                    ... Save results in concurrent dictionary
    }

我使用此代码进行了一些测试,并且将线程数从1增加到2到3等等。不会使进程运行得更快。看起来线程正在等待互操作调用完成,这让我觉得它出于某种原因使用单线程。

以下是Interop方法的定义:

namespace MultiLanguage
{
    using System;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Security;

[ComImport, InterfaceType((short) 1), Guid("DCCFC164-2B38-11D2-B7EC-00C04F8F5D9A")]
public interface IMultiLanguage2

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)]
    void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage,
        [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, 
        [In, Out] ref DetectEncodingInfo lpEncoding, 
        [In, Out] ref int pnScores);

我有什么办法可以让它使用多个线程吗?我注意到唯一需要单线程的是MethodImplOptions.Synchronized,但在这种情况下没有使用它。

EncodingTools.cs的代码取自此处: http://www.codeproject.com/Articles/17201/Detect-Encoding-for-In-and-Outgoing-Text

2 个答案:

答案 0 :(得分:3)

  ... Load File bytes

当您的计算机具有多个处理器核心时,线程可以加速您的程序,这些天很容易获得。但是,您的程序可能会花费大量时间在这个看不见的代码上,与现代处理器的原始处理速度相比,磁盘I / O非常慢。而且你仍然只有一个磁盘,根本就没有并发性。线程将轮流等待从磁盘读取数据。

[ComImport, InterfaceType((short) 1), Guid("DCCFC164-2B38-11D2-B7EC-00C04F8F5D9A")]
public interface IMultiLanguage2

这是一个COM接口,由CMultiLanguage coclass实现。您可以使用Regedit.exe在注册表中找到它,HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{275C23E2-3747-11D0-9FEA-00AA003F8646}键包含此coclass的配置。线程是在COM中留给客户端程序员的细节,COM coclass声明它使用ThreadingModel键支持哪种线程。

CMultiLanguage的值为“Both”。这是个好消息,但现在对于你如何创建对象非常重要。如果在STA线程(Winforms或WPF项目中的主线程的默认值)上创建对象,则COM通过封送从工作线程到STA线程的接口方法调用来确保所有代码保持线程安全。这将导致并发性丢失,线程轮流进入单线程单元。

只有在MTA线程上创建对象时才能获得并发性。您从线程池线程或您自己的线程获得的类型,而不调用其SetApartmentState()方法。确保这一点的一个明显方法是在工作线程本身上创建CMultiLanguage对象,并避免让这些工作线程共享同一个对象。

在开始修复之前,首先需要确定程序中的瓶颈。首先关注文件加载并确保获得真实的测量结果,避免一遍又一遍地在同一组文件上运行测试程序。这会产生不切实际的好结果,因为文件数据将从文件系统缓存中读取。只有重新启动或文件系统缓存重置后的第一次测试才能为您提供可靠的测量。 SysInternals的RamMap实用程序对此非常有用,在开始测试之前使用其Empty + Empty Standby List菜单命令,以便能够比较苹果和苹果。

如果这表明文件加载是瓶颈,那么你就完成了,只有改进的硬件可以解决这个问题。但是,如果您测量了IMultiLanguage2调用,那么请关注CMultiLanguage对象的使用。如果没有保证您可以获得成功,COM服务器通常会通过为您提供锁定来提供线程安全性。这种隐藏的锁定可能会破坏你获得并发的几率。获得成功的唯一方法是让一个线程中的文件读取与另一个线程中的解析重叠。

答案 1 :(得分:0)

尝试使用参数/ apartment = MTA

运行nunit-console