Простая программа простых чисел - странная проблема с потоками C # - PullRequest
2 голосов
/ 01 января 2011

Это мой код:

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

namespace FirePrime
{
    class Program
    {
        static bool[] ThreadsFinished;
        static bool[] nums;

        static bool AllThreadsFinished()
        {
            bool allThreadsFinished = false;
            foreach (var threadFinished in ThreadsFinished)
            {
                allThreadsFinished &= threadFinished;
            }
            return allThreadsFinished;
        }

        static bool isPrime(int n)
        {
            if (n < 2) { return false; }
            if (n == 2) { return true; }
            if (n % 2 == 0) { return false; }
            int d = 3;
            while (d * d <= n)
            {
                if (n % d == 0) { return false; }
                d += 2;
            }
            return true;
        }

        static void MarkPrimes(int startNumber,int stopNumber,int ThreadNr)
        {
            for (int j = startNumber; j < stopNumber; j++)
                nums[j] = isPrime(j);
            lock (typeof(Program))
            {
                ThreadsFinished[ThreadNr] = true;
            }
        }

        static void Main(string[] args)
        {
            int nrNums = 100;
            int nrThreads = 10;
            //var threadStartNums = new List<int>();

            ThreadsFinished = new bool[nrThreads];

            nums = new bool[nrNums];
            //var nums = new List<bool>();
            nums[0] = false;
            nums[1] = false;
            for(int i=2;i<nrNums;i++)
                nums[i] = true;

            int interval = (int)(nrNums / nrThreads);
            //threadStartNums.Add(2);
            //int aux = firstStartNum;
            //int i = 2;
            //while (aux < interval)
            //{
            //    aux = interval*i;
            //    i=i+1;
            //    threadStartNums.Add(aux);
            //}

            int startNum = 0;

            for (int i = 0; i < nrThreads; i++)
            {

                var _thread = new System.Threading.Thread(() => MarkPrimes(startNum, Math.Min(startNum + interval, nrNums), i));
                startNum = startNum + interval;
                //set the thread to run in the background
                _thread.IsBackground = true;
                //start our thread
                _thread.Start();
            }

            while (!AllThreadsFinished())
            {
                Thread.Sleep(1);
            }

            for (int i = 0; i < nrNums; i++)
                if(nums[i])
                    Console.WriteLine(i);
        }
    }
}

Это должна быть довольно простая программа, которая должна находить и выводить первые nrNums простые числа с использованием потоков nrThreads, работающих параллельно.

Итак, я просто разделил nrNums на nrThreads равных кусков (ну, последний не будет равен; если nrThreads не делится на nrNums, он также будет содержать остаток, курс).

Я запускаю nrThreads темы.

Все они проверяют каждое число в соответствующем куске и проверяют, простое оно или нет; они помечают все в массиве bool, в котором хранятся все простые числа.

Все потоки превращают определенный элемент в другом логическом массиве ThreadsFinished в истину, когда они заканчивают.

Теперь начинается странная часть:

Потоки никогда не заканчиваются. Если я отлаживаю, я обнаружил, что ThreadNr - это не то, что я назначаю ему в цикле, а другое значение. Я предполагаю, что это нормально, поскольку потоки выполняются впоследствии, и к тому времени счетчик (переменная i) уже увеличивается, но я не могу понять, как сделать код правильным.

Может кто-нибудь помочь?

Заранее спасибо.

П.С .: Я знаю, что алгоритм не очень эффективен; Я стремлюсь к решению, используя сито Eratosthenes также с x заданными нитями. Но сейчас я даже не могу заставить его работать, и я не нашел примеров реализации этого алгоритма нигде на понятном мне языке.

1 Ответ

5 голосов
/ 01 января 2011

Значение, которое получает поток, - это значение, которое startNum хранит при запуске потока. Для его решения скопируйте значение в локальную переменную:

for (int i = 0; i < nrThreads; i++)
{
 var localStartNum = startNum; // save value in local variable
                                  // and use in the thread start
    var localIndex = i;

 var _thread = new System.Threading.Thread(() => 
                      MarkPrimes(localStartNum,
                                 Math.Min(localStartNum + interval, nrNums),
                                 localIndex));
 startNum = startNum + interval;
 _thread.IsBackground = true;
 _thread.Start();
}

Еще одна ошибка в коде ожидает всех потоков:

static bool AllThreadsFinished()
{
    bool allThreadsFinished = true; // Initialize to true instead of false
                                    // Otherwise, all ANDs will result false
    foreach (var threadFinished in ThreadsFinished)
    {
        allThreadsFinished = threadFinished;
    }

    return allThreadsFinished;
}

Один совет, который может немного помочь в синхронизации потоков: вы можете сохранить все потоки в списке и присоединить их к основному потоку.

var threads = new List<Thread>();

for (int i = 0; i < nrThreads; i++)
{
 var localStartNum = startNum; // save value in local variable
                                  // and use in the thread start
 var _thread = new System.Threading.Thread(() => 
                      MarkPrimes(localStartNum,
                                 Math.Min(localStartNum + interval, nrNums), i));
 startNum = startNum + interval;
 _thread.IsBackground = true;
 _thread.Start();
    threads.Add(_thread);
}

foreach(var thread in threads)
{
    thread.Join();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...