0

我有以下使用新的 .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

4

2 回答 2

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() 方法即可从线程池线程或您自己的 Thread 获得的类型。确保这一点的一个明显方法是在工作线程本身上创建 CMultiLanguage 对象,并避免让这些工作线程共享同一个对象。

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

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

于 2013-07-08T11:06:49.503 回答
0

尝试使用参数 /apartment=MTA 运行 nunit-console

于 2013-07-09T05:38:26.580 回答