用于优化的线程用法

时间:2010-12-16 21:00:55

标签: c# multithreading performance asynchronous delegates

这是C#中的一段代码,它对双精度矩阵的每一行应用一个操作(假设为200x200)。

For (int i = 0; i < 200; i++)
{
   result = process(row[i]);
   DoSomething(result);
}

流程 是一个静态方法,我有一个 Corei5 CPU和 Windows XP .Net Framework 3.5 。为了获得性能,我尝试使用单独的线程处理每一行(使用 Asynchronous delegates )。所以我重写了如下代码:

List<Func<double[], double>> myMethodList = new List<Func<double[], double>>();
List<IAsyncResult> myCookieList = new List<IAsyncResult>();
for (int i = 0; i < 200; i++)
{
   Func<double[], double> myMethod = process;
   IAsyncResult myCookie = myMethod.BeginInvoke(row[i], null, null);
   myMethodList.Add(myMethod);
   myCookieList.Add(myCookie);
}
for (int j = 0; j < 200; j++)
{
   result = myMethodList[j].EndInvoke(myCookieList[j]);
   DoSomething(result);
}

此代码在一次运行中被调用1000个矩阵。当我测试时,令人惊讶的是我没有得到任何性能提升!所以这给我提出了这个问题,即在多数情况下多线程对提高性能有好处,而且我的代码也符合逻辑?

6 个答案:

答案 0 :(得分:2)

通过使用BeginInvoke调用AsyncCallback,您可以实现更多并行性(在结果处理中) - 这将在ThreadPool线程中执行结果处理,而不是内联你现在有。

请参阅异步编程文档的最后一部分here

在您做任何修改代码的事情之前,您应该对其进行分析,以找出该程序花费时间的位置。

答案 1 :(得分:2)

乍一看,您的代码看起来不错。也许CPU不是瓶颈。

您能否确认process()DoSomething()是独立的,并且不对共享资源执行任何I / O或锁定?

这里的要点是你必须开始测量。

当然,使用TPL的Fx4使这种事情更容易编写,并且通常更有效率。

答案 2 :(得分:1)

你的代码有点过分了。看看循环;对于200次迭代中的每次迭代,您都要创建一个新线程来进行异步调用。这将导致您的进程具有201个活动线程。有一个递减收益的法则;大约是线程数量的两倍,因为处理器具有“执行单元”的数量(CPU数量,每个CPU上核心数量的倍数,X2,如果核心可以是超线程的),您的计算机将开始消费调度线程的时间多于运行它们的时间。最先进的服务器有4个四核HT CPU,大约32个EU。 200个主动执行的线程将使该服务器崩溃并哭泣。

如果处理顺序无关紧要,我会实现类似MergeSort的算法;将数组分成两半,处理左手,处理右手。每个“左手”都可以由新线程处理,但是处理当前线程中的“右手”。然后,实现一些线程安全的手段,将线程数限制为“执行单元”数量的1.25倍;如果已达到限制,则继续线性处理,而不创建新线程。

答案 3 :(得分:0)

由于您处理EndInvoke方法调用的方式,看起来您没有获得任何性能。由于您使用BeginInvoke调用“process”,因此这些函数调用会立即返回,因此第一个循环可能很快就会完成。但是,EndInvoke会阻塞,直到调用它的调用完成处理,您仍然按顺序使用它。正如史蒂夫所说,你应该使用AsyncCallback,以便每个完成事件都在它自己的线程上处理。

答案 4 :(得分:0)

你没有看到太大的收获,因为你没有并行化代码,是的,你正在做异步,但这只是意味着你的循环不等待计算才能进入下一步。使用Parallel.For而不是for循环,看看你的多核盒子上是否有任何增益...

答案 5 :(得分:0)

如果您打算使用异步委托,这将是确保在线程池线程上发生回调的方法;

        internal static void Do()
    {
        AsyncCallback cb = Complete;

        List<double[]> row = CreateList();
        for (int i = 0; i < 200; i++)
        {
            Func<double[], double> myMethod = Process;
            myMethod.BeginInvoke(row[i], cb, null);
        }
    }
    static double Process (double[] vals)
    {
       // your implementation
        return randy.NextDouble();
    }
    static void Complete(IAsyncResult token)
    {

        Func<double[], double> callBack = (Func<double[], double>)((AsyncResult)token).AsyncDelegate;
        double res = callBack.EndInvoke(token);

        Console.WriteLine("complete res {0}", res);
        DoSomething(res);


    }