烘焙算法中的重复数学结果(C#代码)

时间:2015-04-20 12:19:25

标签: c# multithreading algorithm math

Index out of bounds when create new thread with parameters? - 继续上一个话题,现在我的面包店算法代码出现了新问题!

这是我的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BakeryAlgorithm
{
    class Program
    {
        static int threads = 10;
        static string x = "";
        static int count = 0;
        static int[] ticket = new int[threads];
        static bool[] entering = new bool[threads];

        public static void doLock(int pid)
        {
            for (int i = 0; i < threads; i++)
            {
                ticket[i] = 0;
                entering[i] = false;
            }
                entering[pid] = true;

            int max = 0;

            for (int i = 0; i < threads; i++)
            {
                if (ticket[i] > ticket[max]) { max = i; }
            } 

            ticket[pid] = 1+max;
            entering[pid] = false;


            for (int i = 0; i < threads; ++i)
            {
                if (i != pid)
                {
                    while (entering[i]) 
                    {
                        Thread.Yield();   
                    } 
                    while (ticket[i] != 0 && (ticket[pid] > ticket[i] ||
                              (ticket[pid] == ticket[i] && pid > i)))
                    {
                        Thread.Yield();
                    }
                }
            }  
           if (x == "C" || x == "c")
Console.WriteLine("[System] PID " + pid.ToString() + " get    into critical section"); 
        }

        public static void unlock(int pid)
        {
            ticket[pid] = 0;
            count++;
            Console.WriteLine("[Thread] PID " + pid.ToString() + " complete.");
        }

        public static void arrayInit()
        {
            for (int i = 0; i < threads; i++)
            {
                ticket[i] = 0;
                entering[i] = false;
            }
        }

        public static void simThread(int i)
        {
            doLock(i);
            if (x == "C" || x=="c")
            Console.WriteLine("[Thread] PID " + i.ToString() + " begin to process...");
            //Do some thing ????
            Random rnd = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
            int a = rnd.Next(1,99); 
            int b = rnd.Next(1,99);
            int c = rnd.Next(1,4);

            int d = 0;
            string o="";

            if (c == 1)
            {
                d = a + b;
                o="+";
            }
            else if (c == 2)
            {
                d = a * b;
                o="*";
            }
            else if (c == 3)
            {
                d = a / b;
                o="/";
            }
            else
            {
                d = a - b;
                o="-";
            }

            if (x == "C" || x == "c")
                Console.WriteLine("Math Result : " + a.ToString() + o + b.ToString() + "=" + d.ToString());
            unlock(i);
        }
        [STAThread]
        static void Main(string[] args)
        {
            arrayInit();
            string choice="C";
            while (choice == "C" || x == "c")
            {
                        Console.WriteLine("Process log (C=Yes,K=No) : ");
                        x = Console.ReadLine();
                        if (x == "")
                            x = "C";

                Console.Clear();
                Console.WriteLine("----------------------------------");
                Console.WriteLine("Bakery Algorithm C#");
                Console.WriteLine("Number of threads : " + threads.ToString());
                Console.WriteLine("Process Log...");
                Console.WriteLine("----------------------------------");

                Thread[] threadArray = new Thread[threads];
                for (int i = 0; i < 10; i++)
                {
                    int copy = i;
                    threadArray[i] = new Thread(() => simThread(copy));
                    if (x == "C" || x == "c")
                    Console.WriteLine("[System] PID " + i.ToString() + " created");
                    threadArray[i].Start();
                }

                Console.ReadLine();
                Console.WriteLine("----------------------------------");
                Console.WriteLine("Complete processed " + count.ToString() + " threads !");
                count = 0;
                Console.WriteLine("----------------------------------");


                        Console.WriteLine("You want to restart  (Yes=C or No=K)");
                        choice = Console.ReadLine();
                        if (choice == "")
                            choice = "C";
            }
        }
    }
}

结果如下:

2*2=4
2*2=4 << duplicated
3*2=6
4*2=8
4*6=24
4*2=8 << duplicated

....并继续重复值(随机位置)!

希望有人在这里帮忙!

3 个答案:

答案 0 :(得分:3)

您的代码存在许多问题,但最重要的部分是您没有阅读使Lamport的面包店工作的要求:

  

Lamport的面包店算法采用顺序一致性记忆模型。

您将很难找到具有连续一致性的现代计算机。

因此,即使您的实现在这些约束方面是正确的,在几乎任何运行.NET的计算机上仍然是错误的。为了在现代CPU和.NET上工作,你需要插入内存屏障以防止指令重新排序并引入缓存刷新以确保每个CPU核心看到相同的值......然后你可能会更好关闭使用不同的同步原语。

现在,修复这些类型的算法往往相当困难 - 多线程本身很难,无锁多线程只会将其推向荒谬的领域。所以,我只想谈谈一些问题:

1)你不能只使用new Random()并期望从中获得统计随机数。 Random有一个内部状态,默认情况下初始化为当前操作系统标记 - 这意味着连续创建10 Random然后对每个Next执行ThreadLocal<Random> rnd = new ThreadLocal<Random>(() => new Random(Guid.NewGuid().GetHashCode())); 很可能产生完全相同的“随机”数字。

优雅地处理它的一种方法是拥有一个线程局部字段:

rnd.Value.Next(...)

然后,您的每个线程都可以安全地执行Random并获得可靠的数字而无需锁定。

但是,由于此练习的重点是允许共享访问可变状态,因此更符合任务的解决方案是使用单个共享doLock字段(仅在创建之前创建一次)线程)。由于Bakery算法应该确保您可以安全地使用临界区中的共享内容,如果正确实现,这应该是安全的:)

2)要真正使Bakery部分工作,你需要强制执行唯一正确的指令。

这是 hard 。严重。

我真的不确定如何安全地做到这一点。

最佳启动方式是在每次读取和写入共享状态之前和之后插入显式内存屏障。然后你可以逐个去除那些不必要的东西。当然,您只需在unlocksimThread方法中使用此方法 - Thread.MemoryBarrier(); entering[pid] = true; Thread.MemoryBarrier(); int max = 0; for (int i = 0; i < threads; i++) { if (ticket[i] > ticket[max]) { max = i; } } Thread.MemoryBarrier(); ticket[pid] = 1+max; Thread.MemoryBarrier(); entering[pid] = false; Thread.MemoryBarrier(); 的其余部分应为单线程。

简短的样本:

ticket[i]

那么,哪一个可以安全删除?我不知道。我必须使用大量的精神力量来确保这是安全的。哎呀,我不确定它是否安全 - 我是否还需要重写for循环? ticket[max]lock是否足够新鲜,算法才能正常运行?我知道有些肯定需要,但我不确定哪个可以安全地被遗漏。

我很确定这比使用简单的{{1}}慢。对于任何生产代码,避开这样的代码 - “智能”代码通常会让您遇到麻烦,即使您团队中的每个人都理解它。很难找到那些专家,而且大多数人都不会像长米棒一样接触无锁代码:)

答案 1 :(得分:1)

您必须为每个帖子(more details

创建不同的随机数

请在主方法中尝试此代码:

for (int i = 0; i < 10; i++)
{
     int temp = i;
     threadArray[i] = new Thread(() => simThread(temp));
     Console.WriteLine("[He Thong] PID " + i.ToString() + " duoc khoi tao");
     threadArray[i].Start();
     Thread.Sleep(20);
}

以及线程中的以下代码:

Random rand = new Random((int) DateTime.Now.Ticks & 0x0000FFFF);

现在您可以确保为每个线程生成不同的随机数。

答案 2 :(得分:0)

尝试:

Random rnd = new Random(Environment.TickCount / (i + 1));

这将为每个RNG提供不同的种子。