代码之家  ›  专栏  ›  技术社区  ›  Lukas Å alkauskas

使用C(问题)中的多线程加速循环

  •  13
  • Lukas Å alkauskas  · 技术社区  · 16 年前

    假设我有一个函数,它通过一百万个/十亿个字符串并检查其中的smth。

    F.EX:

    foreach (String item in ListOfStrings)
    {
        result.add(CalculateSmth(item));
    }
    

    它消耗大量的时间,因为计算是非常耗时的函数。

    我想问:如何在这种进程中集成多线程?

    我想启动5个线程,每个线程都返回一些结果,直到列表中有项目为止。

    也许任何人都可以展示一些例子或文章。

    忘了在.NET 2.0中提到我需要它

    6 回复  |  直到 14 年前
        1
  •  17
  •   Tobi    14 年前

    你可以试试 Parallel extensions (.NET 4.0的一部分)

    这些允许您编写如下内容:

    Parallel.Foreach (ListOfStrings, (item) => 
        result.add(CalculateSmth(item));
    );
    

    当然,result.add需要是线程安全的。

        2
  •  18
  •   noocyte    16 年前

    并行扩展是很酷的,但是也可以这样使用threadpool:

    using System.Collections.Generic;
    using System.Threading;
    
    namespace noocyte.Threading
    {
        class CalcState
        {
            public CalcState(ManualResetEvent reset, string input) {
                Reset = reset;
                Input = input;
            }
            public ManualResetEvent Reset { get; private set; }
            public string Input { get; set; }
        }
    
        class CalculateMT
        {
            List<string> result = new List<string>();
            List<ManualResetEvent> events = new List<ManualResetEvent>();
    
            private void Calc() {
                List<string> aList = new List<string>();
                aList.Add("test");
    
                foreach (var item in aList)
                {
                    CalcState cs = new CalcState(new ManualResetEvent(false), item);
                    events.Add(cs.Reset);
                    ThreadPool.QueueUserWorkItem(new WaitCallback(Calculate), cs);
                }
                WaitHandle.WaitAll(events.ToArray());
            }
    
            private void Calculate(object s)
            {
                CalcState cs = s as CalcState;
                cs.Reset.Set();
                result.Add(cs.Input);
            }
        }
    }
    
        3
  •  12
  •   slim    16 年前

    注意并发性并不会给你更多的资源。你需要确定什么在减慢计算速度。

    例如,如果它是CPU绑定的(并且您在一个核心上),那么相同数量的CPU计时将会转到代码中,不管您是按顺序还是并行方式执行它们。另外,管理线程会带来一些开销。相同的参数适用于其他约束(例如I/O)

    只有当calculatesMth在执行过程中使资源保持空闲状态时,才能获得性能提升,而这可以被另一个实例使用。这并不少见。例如,如果任务涉及到IO,然后是一些CPU内容,那么进程1可以在进程2执行IO时执行CPU内容。正如Mats指出的那样,如果你有基础设施,一系列生产者-消费者单元可以实现这一点。

        4
  •  5
  •   Hallgrim    16 年前

    你需要把你想同时做的工作分开。以下是如何将工作拆分为两部分的示例:

    List<string> work = (some list with lots of strings)
    
    // Split the work in two
    List<string> odd = new List<string>();
    List<string> even = new List<string>();
    for (int i = 0; i < work.Count; i++)
    {
        if (i % 2 == 0)
        {
            even.Add(work[i]);
        }
        else
        {
            odd.Add(work[i]);
        }
    }
    
    // Set up to worker delegates
    List<Foo> oddResult = new List<Foo>();
    Action oddWork = delegate { foreach (string item in odd) oddResult.Add(CalculateSmth(item)); };
    
    List<Foo> evenResult = new List<Foo>();
    Action evenWork = delegate { foreach (string item in even) evenResult.Add(CalculateSmth(item)); };
    
    // Run two delegates asynchronously
    IAsyncResult evenHandle = evenWork.BeginInvoke(null, null);
    IAsyncResult oddHandle = oddWork.BeginInvoke(null, null);
    
    // Wait for both to finish
    evenWork.EndInvoke(evenHandle);
    oddWork.EndInvoke(oddHandle);
    
    // Merge the results from the two jobs
    List<Foo> allResults = new List<Foo>();
    allResults.AddRange(oddResult);
    allResults.AddRange(evenResult);
    
    return allResults;
    
        5
  •  2
  •   Mats Wiklander    16 年前

    您必须回答的第一个问题是是否应该使用线程

    如果您的函数calculatesMth()基本上是CPU限制的,即CPU使用量很大,基本上没有I/O使用量,那么我很难看到使用线程的意义,因为线程将在相同的资源上竞争,在本例中是CPU。

    如果calculatesMth()同时使用CPU和I/O,那么它可能是使用线程的一个要点。

    我完全同意我的回答。我做了一个错误的假设,我们说的是单核CPU,但是现在我们有多核CPU,我的错。

        6
  •  1
  •   Mats Fredriksson    16 年前

    不是说我现在有什么好文章,但你想做的是和生产者消费者一起使用线程工具。

    生产者循环并创建任务(在本例中,可能只是将列表或堆栈中的项目排队)。例如,使用者是五个线程,它们从堆栈中读取一个项目,通过计算消耗它,然后将它存储在其他地方。

    这样,多线程仅限于这五个线程,在堆栈为空之前,它们都有工作要做。

    需要考虑的事项:

    • 对输入和输出列表进行保护,例如互斥。
    • 如果订单很重要,请确保维护输出订单。一个例子是将它们存储在排序列表或类似的东西中。
    • 确保calculatesMth是线程安全的,不使用任何全局状态。