使用parallel时程序卡住了

时间:2015-04-16 09:45:37

标签: c# multithreading parallel.for

我是C#的新手,我制作了一个简单的程序来模拟乐透绘图。它需要第一个(随机)数字并计算获胜所需的数量。这是一个波兰乐透,所以有6个数字可以匹配。

当程序以简单的for循环运行时,一切正常。但是当我使用Parallel For或其他任何多任务或多线程选项时,会出现问题。

首先是代码:

class Program
{
    public static int howMany = 100;

    static void Main(string[] args)
    {

        Six my;
        Six computers;
        long sum = 0;
        double avg = 0;
        int min = 1000000000;
        int max = 0;



        for (int i = 0; i < howMany; i++)
        {
            my = new Six();
            Console.WriteLine((i + 1).ToString() + " My: " + my.ToString());

            int counter = 0;
            do
            {
                computers = new Six();
                counter++;
            } while (!my.Equals(computers));
            Console.WriteLine((i + 1).ToString() + " Computers: " + computers.ToString());
            Console.WriteLine(counter.ToString("After: ### ### ###") + "\n");
            if (counter < min)
                min = counter;
            if (counter > max)
                max = counter;
            sum += counter;
        }

        avg = sum / howMany;
        Console.WriteLine("Average: " + avg);
        Console.WriteLine("Sum: " + sum);
        Console.WriteLine("Min: " + min);
        Console.WriteLine("Max: " + max);

        Console.Read();
    }

}







class Six : IEquatable<Six>
{
    internal byte first;
    internal byte second;
    internal byte third;
    internal byte fourth;
    internal byte fifth;
    internal byte sixth;
    private static Random r = new Random();

    public Six()
    {

        GenerateRandomNumbers();
    }

    public bool Equals(Six other)
    {
        if (this.first == other.first
            && this.second == other.second
            && this.third == other.third
            && this.fourth == other.fourth
            && this.fifth == other.fifth
            && this.sixth == other.sixth)
            return true;
        else
            return false;
    }

    private void GenerateRandomNumbers()
    {
        byte[] numbers = new byte[6];
        byte k = 0;
        for (int i = 0; i < 6; i++)
        {
            do
            {
                k = (byte)(r.Next(49) + 1);
            }while (numbers.Contains(k));

            numbers[i] = k;
            k = 0;
        }

        Array.Sort(numbers);

        this.first = numbers[0];
        this.second = numbers[1];
        this.third = numbers[2];
        this.fourth = numbers[3];
        this.fifth = numbers[4];
        this.sixth = numbers[5];
    }

    public override string ToString()
    {
        return this.first + ", " + this.second + ", " + this.third + ", " + this.fourth + ", " + this.fifth + ", " + this.sixth;
    }

}

当我尝试将其设为Parallel.For:

        long sum = 0;
        double avg = 0;
        int min = 1000000000;
        int max = 0;


        Parallel.For(0, howMany, (i) =>
        {
            Six my = new Six();
            Six computers;
            Console.WriteLine((i + 1).ToString() + " My: " + my.ToString());

            int counter = 0;
            do
            {
                computers = new Six();

                // Checking when it's getting stuck
                if (counter % 100 == 0)
                    Console.WriteLine(counter);

                counter++;
            } while (!my.Equals(computers));
            Console.WriteLine((i + 1).ToString() + " Computers: " + computers.ToString());
            Console.WriteLine(counter.ToString("After: ### ### ###") + "\n");

            // It never get to this point, so there is no problem with "global" veriables
            if (counter < min)
                min = counter;
            if (counter > max)
                max = counter;
            sum += counter;
        });

程序在某些时候陷入困境。计数器达到~3,000-40,000并且拒绝进一步。

我尝试了什么:

  1. 使类成为结构
  2. 每1000次迭代收集垃圾
  3. 使用ThreadPool
  4. 使用Task.Run
  5. 仅制作随机课程计划会员(厌倦使六级课程“更轻”)
  6. 但我一无所获。

    我知道这对你们中的某些人来说可能是一件非常简单的事情,但是男人必须以某种方式学习;)我甚至买了一本关于异步编程的书,以找出它为什么不起作用,但是无法理解它进行。

2 个答案:

答案 0 :(得分:1)

Random不是线程安全的......

等待代码停止在并行版本和暂停中写入新行。这会停止所有线程。您会注意到所有并行线程都在while循环中。

数字数组都是1,0,0,0,0,0而r.Next只返回1.字节数组总是包含的数字。所以,你打破了Random

要解决此问题,您需要通过在每次访问r.Next或将静态声明更改为

时锁定r来使r线程安全。
private static readonly ThreadLocal<Random> r
     = new ThreadLocal<Random>(() => new Random());

Next来电变为

k = (byte)(r.Value.Next(49) + 1);

这将为每个线程创建一个新的静态Random实例。

正如您所指出的那样,同时创建大量的Random会产生相同的数字序列,以便绕过这个添加种子类

static class RGen
{
    private static Random seedGen = new Random();

    public static Random GetRGenerator()
    {
        lock (seedGen)
        {
            return new Random(seedGen.Next());
        }
    }
}

并将声明更改为

private static readonly ThreadLocal<Random> r
     = new ThreadLocal<Random>(() => RGen.GetRGenerator());

这将确保每个新的随机实例具有不同的种子值。

答案 1 :(得分:0)

我找到了一个基于James Barrass写的解决方案:

public static readonly ThreadLocal<Random> r = new ThreadLocal<Random>(() => new Random(Thread.CurrentThread.ManagedThreadId + DateTime.Now.Miliseconds));

这使程序运行良好:)