Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Несколько потоков работают с ошибкой


Автор: Wecoyote 18.4.2010, 06:34
Всем привет. Бьюсь над мультипоточностью уже вторые сутки.

Заранее прошу меня извинить, описать проблему без приведения своего запутанного кода не смог.

В коде приведённом ниже открываются текстовые файлы и разделяются на неповторяющиеся слова. (различные слова)

В один поток все выполняется правильно и без ошибок.

Если запускаю столько потоков, сколько ядер процессора - то, при одинаковых исходных данных - в результате разное количество слов, что по определению невозможно. Либо ошибка (индекс вне границ массива).

Если кому-нибудь не лень разобраться в моём коде и указать на ошибку - буду весьма признателен.


Класс Program.cs - здесь вызываются потоки обрабатывающие файлы:

Код

using System;
using System.IO;
using System.Text;
using System.Threading;

namespace DWS_9L
{
    class Program
    {
        public static FileInfo[] textsToRead; // Обрабатываемые файлы
        
        public static int filesNum = 0; // Кол-во обрабатываемых файлов
        public static int finishedFilesNum = 0; // Кол-во обработанных файлов
        
        public static string[][] readedWords = new string[1185922][]; // Этот массив нужно заполнить
        public static int[] readedWordsIndexWordsNum = new int[1185922]; // Кол-во элементов в массивах readedWords[x]
        public static bool[] levelBlocked = new bool[1185922];
        
        public static int Main(string[] args)
        {
            DirectoryInfo bookDir = new DirectoryInfo("./texts");
            textsToRead = bookDir.GetFiles();
            filesNum = textsToRead.Length;
            
            for(int i=0; i<1185922; i++){
                readedWordsTreeLevelWordsNum[i] = 0;
                levelBlocked[i] = false;
            }
            
            int pc = Environment.ProcessorCount;
            
            Splitter[] splitters = new Splitter[pc]; // Создание собственно "обработчиков"
            Thread[] splitterThreads = new Thread[pc];
            
            for(int i=0; i<pc; i++){
             splitters[i] = new Splitter();
            }
            
            for(int i=0; i<pc; i++){
             splitterThreads[i] = new Thread(new ThreadStart(splitters[i].splitWords));
             splitterThreads[i].Priority = ThreadPriority.AboveNormal;
            }
            
            for(int i=0; i<pc; i++){
             splitterThreads[i].Start();
            }
            
            for(int i=0; i<pc; i++){
             splitterThreads[i].Join();
            }
            
            //-- Здесь вывод результатов --
            
            Console.Write("\aPress any key to continue . . . ");
            Console.ReadKey(true);
            return 0;
        }
    }
}


Класс Splitter.cs - собственно обработчик:
Код

using System;
using System.IO;
using System.Threading;

namespace DWS_9L
{
    public class Splitter
    {
        public Splitter()
        {
        }
        
        public void splitWords(){
            while(Program.finishedFilesNum < Program.filesNum){ // Узнаём есть ли еще необработанные файлы
                int localFileNum = Program.finishedFilesNum; // Запоминаем номер файла с которым работает поток
                Program.finishedFilesNum++; // Сообщаем остальным потокам, что файл занят
                
                //-- Здесь код, который получает из файла массив слов string outWords[] --
                
                for(int z=0; z < outWords.Length; z++){ // Перебираем слова
                    //-- Здесь код, определяющий int sum - первый индекс массива в который нужно добавить слово
                    
                    bool done = false;
                    while(!done){
                        if(!Program.levelBlocked[sum]){ // Если другие потоки не работают в данный момент с индексом массива [sum]
                            Program.levelBlocked[sum] = true; // Сообщаем остальным потокам, что этот индекс занят
                            
                            if(Program.readedWordsIndexWordsNum[sum] != 0){ // Если в массиве Program.readedWords[sum] есть хоть один элемент
                                bool flag = false; // Флаг, обозначающий - уже добавлено слово в массив или ещё нет
                                for(int y=0; y<Program.readedWordsIndexWordsNum[sum]; y++){ // Ищем слово outWords[z] в массиве Program.readedWords[sum]
                                    if(Program.readedWords[sum][y] == outWords[z]){
                                        flag = true; // Если нашли
                                        break;
                                    }
                                }
                                if(!flag){ // Если слово не найдено - добавляем
                                    string[] tmpString = new string[Program.readedWordsIndexWordsNum[sum]];
                                    tmpString = Program.readedWords[sum];
                                    Program.readedWords[sum] = new string[Program.readedWordsIndexWordsNum[sum] + 1];
                                    tmpString.CopyTo(Program.readedWords[sum], 0);
                                    Program.readedWords[sum][Program.readedWordsIndexWordsNum[sum]] = outWords[z]; // Строка с ошибкой (индекс находился вне границ массива)
                                    Program.readedWordsIndexWordsNum[sum]++;
                                }
                            }
                            else{ // Если в массиве Program.readedWords[sum] нет элементов
                                Program.readedWords[sum] = new string[1];
                                Program.readedWords[sum][0] = outWords[z];
                                Program.readedWordsIndexWordsNum[sum]++;
                            }
                            
                            Program.levelBlocked[sum] = false; // Позволяем остальным потокам работать с этим индеском
                            done = true;
                        }
                    }
                }
            }
        }
    }
}

Автор: Heinzz 18.4.2010, 10:08
в Вашем коде не разбирался, но очень похоже на то что не синхронизированно общение с переменной счетчиком или коллекцией. Те к одной переменной без объявления ее временно закрытой нельзя обращаться из разных потоков. Если же Вы все же это делаете, используйте Increment для value type или lock для ссылочных типов.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)