C#查询中的多线程

时间:2011-09-11 17:30:53

标签: c# multithreading

我是C#中多线程的新手。我有一个大小为(x)(y)(z)的3D数组,并且我想要计算每个(x,y)值的所有z样本的平均值。我希望使用多线程(比如说2个线程)来做到这一点,我将发送一半大小的数组(x / 2)* y * z用于处理thread1而另一半用于thread2。

怎么做?如何从各个线程传递和检索参数?代码示例会很有用。

问候

3 个答案:

答案 0 :(得分:8)

我建议您使用PLINQ而不是自己进行线程化。

它将允许您使用LINQ语法运行查询,但会自动并行化(在所有核心上)。

答案 1 :(得分:0)

有很多理由说明使用PLINQ(如Reed所提到的)或Parallel.For实现低开销调度程序来分配多个cpus上的作业有很多理由。

因此,如果我理解正确,也许这可以让你开始(在我的4核机器上,并行版本比单核版本快3倍):

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

class Program
{

    static void AverageOfZ (
        double[] input, 
        double[] result,
        int x, 
        int y, 
        int z
        )
    {
        Debug.Assert(input.Length == x*y*z);
        Debug.Assert(result.Length == x*y);

        //Replace Parallel with Sequential to compare with non-parallel loop
        //Sequential.For(
        Parallel.For(
            0,
            x*y,
            i =>
                {
                    var begin = i*z;
                    var end = begin + z;
                    var sum = 0.0;

                    for (var iter = begin; iter < end; ++iter)
                    {
                        sum += input[iter];
                    }

                    result[i] = sum/z;
                });

    }

    static void Main(string[] args)
    {
        const int X = 64;
        const int Y = 64;
        const int Z = 64;
        const int Repetitions = 40000;

        var random = new Random(19740531);
        var samples = Enumerable.Range(0, X*Y*Z).Select(x => random.NextDouble()).ToArray();
        var result = new double[X*Y];

        var then = DateTime.Now;

        for (var iter = 0; iter < Repetitions; ++iter)
        {
            AverageOfZ(samples, result, X, Y, Z);
        }

        var diff = DateTime.Now - then;
        Console.WriteLine(
            "{0} samples processed {1} times in {2} seconds", 
            samples.Length,
            Repetitions,
            diff.TotalSeconds
            );

    }
}

static class Sequential
{
    public static void For(int from, int to, Action<int> action)
    {
        for (var iter = from; iter < to; ++iter)
        {
            action(iter);
        }
    }
}

PS。在寻求并发性能时,重要的是要考虑不同内核如何访问内存,否则很容易让人失望。

答案 2 :(得分:0)

Dot Net 3.5及其后续版本引入了许多快捷键,这些关键词抽象出了诸如Parallel for Multi threading或Async for Async IO之类的复杂性。不幸的是,这也没有机会理解这些任务中涉及的内容。例如,我的一位同事最近尝试使用Async作为返回身份验证令牌的登录方法。

以下是适用于您的方案的完整多线程示例代码。为了使它更真实,示例代码假装: X是经度 Y是Lattitude 和Z是坐标

处的降雨样本

示例代码也遵循工作单元设计模式,其中每个坐标处的降雨样本成为工作项。它还创建了离散的前台线程,而不是使用后台线程池。 由于工作项的简单性和所涉及的短计算时间,我将线程同步锁分成两个锁。一个用于工作队列,一个用于输出数据。 注意:我没有使用任何Dot网络快捷方式,例如Lync,所以此代码也应该在Dot Net 2.0上运行。

在现实世界的应用程序开发中,只有在复杂的场景中需要类似下面的内容,例如连续的工作项流的流处理,在这种情况下,您还需要实现定期清除的输出数据缓冲区,因为线程将有效地运行永远。

public static class MultiThreadSumRainFall
{
    const int LongitudeSize = 64;
    const int LattitudeSize = 64;
    const int RainFallSamplesSize = 64;
    const int SampleMinValue = 0;
    const int SampleMaxValue = 1000;
    const int ThreadCount = 4;

    public static void SumRainfallAndOutputValues()
    {
        int[][][] SampleData;
        SampleData = GenerateSampleRainfallData();
        for (int Longitude = 0; Longitude < LongitudeSize; Longitude++)
        {
            for (int Lattitude = 0; Lattitude < LattitudeSize; Lattitude++)
            {
                QueueWork(new WorkItem(Longitude, Lattitude, SampleData[Longitude][Lattitude]));
            }
        }
        System.Threading.ThreadStart WorkThreadStart;
        System.Threading.Thread WorkThread;
        List<System.Threading.Thread> RunningThreads;
        WorkThreadStart = new System.Threading.ThreadStart(ParallelSum);
        int NumThreads;
        NumThreads = ThreadCount;
        if (ThreadCount < 1)
        {
            NumThreads = 1;
        }
        else if (NumThreads > (Environment.ProcessorCount + 1))
        {
            NumThreads = Environment.ProcessorCount + 1;
        }
        OutputData = new int[LongitudeSize, LattitudeSize];
        RunningThreads = new List<System.Threading.Thread>();
        for (int I = 0; I < NumThreads; I++)
        {
            WorkThread = new System.Threading.Thread(WorkThreadStart);
            WorkThread.Start();
            RunningThreads.Add(WorkThread);
        }
        bool AllThreadsComplete;
        AllThreadsComplete = false;
        while (!AllThreadsComplete)
        {
            System.Threading.Thread.Sleep(100);
            AllThreadsComplete = true;
            foreach (System.Threading.Thread WorkerThread in RunningThreads)
            {
                if (WorkerThread.IsAlive)
                {
                    AllThreadsComplete = false;
                }
            }
        }
        for (int Longitude = 0; Longitude < LongitudeSize; Longitude++)
        {
            for (int Lattitude = 0; Lattitude < LattitudeSize; Lattitude++)
            {
                Console.Write(string.Concat(OutputData[Longitude, Lattitude], @" "));
            }
            Console.WriteLine();
        }
    }

    private class WorkItem
    {
        public WorkItem(int _Longitude, int _Lattitude, int[] _RainFallSamples)
        {
            Longitude = _Longitude;
            Lattitude = _Lattitude;
            RainFallSamples = _RainFallSamples;
        }
        public int Longitude { get; set; }
        public int Lattitude { get; set; }
        public int[] RainFallSamples { get; set; }
    }

    public static int[][][] GenerateSampleRainfallData()
    {
        int[][][] Result;
        Random Rnd;
        Rnd = new Random();
        Result = new int[LongitudeSize][][];
        for(int Longitude = 0; Longitude < LongitudeSize; Longitude++)
        {
            Result[Longitude] = new int[LattitudeSize][];
            for (int Lattidude = 0; Lattidude < LattitudeSize; Lattidude++)
            {
                Result[Longitude][Lattidude] = new int[RainFallSamplesSize];
                for (int Sample = 0; Sample < RainFallSamplesSize; Sample++)
                {
                    Result[Longitude][Lattidude][Sample] = Rnd.Next(SampleMinValue, SampleMaxValue);
                }
            }
        }
        return Result;
    }

    private static object SyncRootWorkQueue = new object();
    private static Queue<WorkItem> WorkQueue = new Queue<WorkItem>();
    private static void QueueWork(WorkItem SamplesWorkItem)
    {
        lock(SyncRootWorkQueue)
        {
            WorkQueue.Enqueue(SamplesWorkItem);
        }
    }
    private static WorkItem DeQueueWork()
    {
        WorkItem Samples;
        Samples = null;
        lock (SyncRootWorkQueue)
        {
            if (WorkQueue.Count > 0)
            {
                Samples = WorkQueue.Dequeue();
            }
        }
        return Samples;
    }
    private static int QueueSize()
    {
        lock(SyncRootWorkQueue)
        {
            return WorkQueue.Count;
        }
    }

    private static object SyncRootOutputData = new object();
    private static int[,] OutputData;
    private static void SetOutputData(int Longitude, int Lattitude, int SumSamples)
    {
        lock(SyncRootOutputData)
        {
            OutputData[Longitude, Lattitude] = SumSamples;
        }
    }

    private static void ParallelSum()
    {
        WorkItem SamplesWorkItem;
        int SummedResult;
        SamplesWorkItem = DeQueueWork();
        while (SamplesWorkItem != null)
        {
            SummedResult = 0;
            foreach (int SampleValue in SamplesWorkItem.RainFallSamples)
            {
                SummedResult += SampleValue;
            }
            SetOutputData(SamplesWorkItem.Longitude, SamplesWorkItem.Lattitude, SummedResult);
            SamplesWorkItem = DeQueueWork();
        }
    }
}