C # - потокобезопасная реализация класса с фиксированным массивом (очередь / буфер?) (ADD, REMOVE, MODIFY) - PullRequest
1 голос
/ 24 октября 2019

Я новичок в программировании, многопоточности и поточно-безопасной синхронизации в целом, много читал и искал, нашел десятки похожих вопросов - однако не смог найти ничего, что могло бы мне помочь в итоге - или ни моего отсутствиязнание темы не позволило мне заметить / понять ответ.

Я создал класс с 3 полями данных (string, int, double) и 1 method (будетиспользоваться для вычисления нового значения - которое позже будет сравниваться с выбранными критериями фильтрации).

Затем я хочу, чтобы мой основной поток выполнял действия в следующем порядке:

  1. Считывает данные (3 файла) к выбранной структуре данных. - давайте проигнорируем эти
  2. Запускает выбранное количество потоков (2 <= x <= n / 4, n - количество записей в файле), которые будут принимать записи, выполнить метод <strong> для них, и если результат соответствует критериям - запись будет записана в структуру данных результатов .
  3. В структуру данных, которая использовалась для передачи данных через потоки, вставляет всечтение данных.
  4. Ожидание завершения всех потоков.
  5. Запись данных из структуры результатов в файл. - давайте проигнорируем эти

Требования к потокам:

  • Когда все данные обработаны, потоки автоматически завершают работу. Пока работает хотя бы один поток, основной поток ожидает его завершения.
  • Данные в потоки передаются через синхронизированную структуру данных: монитор. Мне нужно реализовать его как класс или структуру, которая содержит методы добавления и удаления.
    • Данные внутри класса монитора сохраняются в массиве (arr.size <= все размеры данных / 2) </li>
    • Если предпринята попытка чтения / удаления с монитора, когда внутренний массивпусто или запись / добавление, когда внутренний массив заполнен, поток заблокирован - через условную синхронизацию потоки выполняются только после того, как какое-то условие выполнено, я полагаю. (Используются методы lock, unlock, Monitor.Wait Pulse и PulseAll).
  • Структура данных результата такая же, как и исходная структура данных, за исключением отсутствия фиксированного размера массива. Данные в структуру результата должны быть добавлены по порядку.

На данный момент у меня есть это (псевдо-иш):

Также я пытался искать разныереализации того, о чем я говорю, и до сих пор с треском проваливались, сейчас 7 часов утра - поэтому код беспорядок, я извиняюсь за это

class Computer // 3 field class.
{
    public string _model { get; set; }
    public int _ram { get; set; }
    public double _screen { get; set; }

    double methodForFilterValue(Computer computer)
    {

        return computer._screen + computer._ram;
    }
}



class Computers // Monitor class.
{
    Computer[] _computers; // array of computers - buffer.
    int _bufferHead { get; set; } // circular buffer properties.
    int _counter { get; set; }
    int _bufferTail { get; set; }
    int _length { get; private set; }

    private readonly object _locker = new Object();

    Computers (int length)
    {
        _bufferHead = _bufferTail = -1;
        _length = length;
        _computers = new Computer[length];
        _counter = 0;
    }

    Computers(Computer[] computers)
    {
        _length = computers.Length;
        _computers = computers;
        _bufferHead = 0;
        _bufferTail = _length - 1;
        _counter = computers.ToList().Count;
    }

    Add(Computer computer)
    {
        lock (_locker)
        {
            if((_bufferHead == 0 && _bufferTail == _length - 1) || (_bufferTail +1 == _bufferHead))
            {
                //means Empty.
                return;
            }
            else
            {
                if (_bufferTail == _length - 1)
                    _bufferTail = 0;
                else
                    _bufferTail++;
                _computers[_bufferTail] = computer;
                _counter++;
            }
            if (_bufferHead == -1)
                _bufferHead = 0;
        }
    }

    Remove()
    {
        lock (_locker){} -- not implemented yet.
    }
}

class ResultStructure // Result class - for filtered records, printing to file.
{
    public Computer[] _resultComputers;
    public int _capacity { get; set; }
    public int _counter { get; set; }

    private readonly object _locker = new Object();
    private const int INITIAL_BUFFER_SIZE = 10;

    public ResultStructure()
    {
        _resultComputers = new Computer[INITIAL_BUFFER_SIZE];
        _capacity = INITIAL_BUFFER_SIZE;
        _counter = 0;
    }
    public ResultStructure(int length)
    {
        _resultComputers = new Computer[length];
        _capacity = length;
        _counter = 0;
    }

    public void AddSorted(Computer computer)
    {
        -- yet to be implemented also.
    }
}

class Program
{
    static void Main(string[] args)
    {
        int size = 90; // single file size: 30 lines.
        string path = AppDomain.CurrentDomain.BaseDirectory;
        string[] filePaths = Directory.GetFiles(file_*.json");
        int selectedCriteria = 20; // each computer's methodForFilterValue() will have to result in a higher value than 20 for it to be added to resultStructure.

        Computers allComputers = new Computers(size/2);
        Computers[] computers = new Computers[filePaths.Length];
        ResultStructure result = new ResultStructure(size);

        **//STEPS:**

        **//1. Data read.**
        for (int i = 0; i < filePaths.Length; i++)
        {
            computers[i] = ReadJson(filePaths[i]);
        }


        **//2. ??? - Need explanation on this part.**
        **//Basically this part about: iterating through each record in computers[i] - calling methodForFilterValue() function, checking if the result of that function is > 20 and if yes, add record to result._resultComputers array. Items should be added while leaving the array sorted. "AddSorted()"**

        **//3. Run threads - addAll items to the same data struct.**
        var threadsAdd = Enumerable.Range(0, filePaths.Length).Select(i => new Thread(() =>
        {
            for (int j = 0; j < computers[i]._computers.Length; j++)
            {
                allComputers.Add(computers[i]._computers[j]);
            }
        }));

        var threads = threadsAdd.ToList();

        foreach (var thread in threads)
        {
            thread.Start();
        }
        **//4. wait for all threads to finish**
        foreach (var thread in threads)
        {
            thread.Join();
        }

        **5.** WriteToFile();

        Write("Program finished execution");
    }

Редактировать:

Какспросил сузит это до: Как создать потокобезопасное добавление, удалить методы - запустить их в нескольких потоках, в то время как в классе Компьютеры, имеющего массив ограниченного размера - блокирование потоков, когда массив пуст или заполнен. Всего записей: 90, массив класса Компьютеры = [45];

Следует также сказать, что я специально обязан не использовать одновременные коллекции.

код, если необходимо !

Ответы [ 2 ]

0 голосов
/ 24 октября 2019

Поскольку вы новичок в многопоточности, ответ о том, что вы должны сделать, прост: lock каждый раз, когда вы получаете доступ к общему ресурсу. «Доступ» означает чтение и запись. «Общий ресурс» означает каждую переменную, поле или свойство, к которым одновременно обращается несколько потоков. Используйте один и тот же объект блокировки для защиты одной и той же переменной. Снимите блокировку как можно скорее.

Этого совета недостаточно для создания работоспособного приложения, а также для избежания взаимоблокировок, но по крайней мере ваши данные не будут повреждены.

0 голосов
/ 24 октября 2019

Имейте в виду, что некоторые параллельные коллекции существуют в C # и должны быть вам полезны.

Вы должны использовать:

ConcurentDictionary<string, Computer>

, если у вас есть идентификатор компьютера.

...