Разрезание времени между пятью потоками в C #

1

Вот описание того, что должна делать программа. Программа должна создать файл и пять потоков для записи в этом файле...

Первый поток должен писать от 1 до 5 в этот файл. Второй поток должен писать от 1 до 10. Третий поток должен писать от 1 до 15. Четвертый поток должен писать от 1 до 20. Пятый поток должен писать от 1 до 25.

Кроме того, должен быть реализован алгоритм, чтобы каждый поток печатал 2 числа и останавливался. следующая нить должна печатать два числа и останавливаться. и так далее, пока все нити не закончат печать их номеров. Вот код, который я разработал до сих пор...

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

namespace ConsoleApplication1
{
    public static class OSAssignment
    {
        // First Thread Tasks...
        static void FirstThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 5; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine("[Thread1] " + i);
                    Thread.Sleep(i);
                }

                else
                {
                    Console.WriteLine("[Thread1] " + i);
                }
            }
        }

        // Second Thread Tasks...
        static void SecondThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 10; i++)
            {
                if (i % 2 == 0)
                {
                    if (i == 10)
                        Console.WriteLine("[Thread2] " + i);

                    else
                    {
                        Console.WriteLine("[Thread2] " + i);
                        Thread.Sleep(i);
                    }
                }

                else
                {
                    Console.WriteLine("[Thread2] " + i);
                }
            }
        }

        // Third Thread Tasks..
        static void ThirdThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 15; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine("[Thread3] " + i);
                    Thread.Sleep(i);
                }

                else
                {
                    Console.WriteLine("[Thread3] " + i);
                }
            }
        }

        // Fourth Thread Tasks...
        static void FourthThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 20; i++)
            {
                if (i % 2 == 0)
                {
                    if (i == 20)
                        Console.WriteLine("[Thread4] " + i);
                    else
                    {
                        Console.WriteLine("[Thread4] " + i);
                        Thread.Sleep(i);
                    }
                }

                else
                {
                    Console.WriteLine("[Thread4] " + i);
                }

            }
        }

        // Fifth Thread Tasks...
        static void FifthThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 25; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine("[Thread5] " + i);
                    Thread.Sleep(i);
                }

                else
                {
                    Console.WriteLine("[Thread5] " + i);
                }

            }
        }

        // Main Function...
        static void Main(string[] args)
        {
            FileStream File = new FileStream("output.txt", FileMode.Create, FileAccess.Write, FileShare.Write);
            StreamWriter Writer = new StreamWriter(File);
            Thread T1 = new Thread(() => FirstThreadTasks(Writer));
            Thread T2 = new Thread(() => SecondThreadTasks(Writer));
            Thread T3 = new Thread(() => ThirdThreadTasks(Writer));
            Thread T4 = new Thread(() => FourthThreadTasks(Writer));
            Thread T5 = new Thread(() => FifthThreadTasks(Writer));
            Console.WriteLine("Initiating Jobs...");
            T1.Start();
            T2.Start();
            T3.Start();
            T4.Start();
            T5.Start();
            Writer.Flush();
            Writer.Close();
            File.Close();
        }
    }
}

Здесь проблемы, с которыми я сталкиваюсь...

  1. Я не могу понять, как сделать 5 потоков записывать в один и тот же файл одновременно с созданием FileShare.Write. Поэтому я просто решил написать консоль на время и разработать алгоритм и посмотреть, как он ведет себя сначала в консоли.

  2. Каждый раз, когда я запускаю программу, результат немного отличается от предыдущего. Всегда бывает, что поток печатает только одно из его номеров в конкретной итерации и продолжает выводить второе число после того, как другой поток завершит свою текущую итерацию.

  3. У меня есть вопрос, который может быть каким-то образом отброшен. Если я удалил Console.WriteLine("Initiating Jobs..."); из основного метода алгоритм не будет вести себя так, как я упоминал в пункте 2. Я действительно не могу понять, почему.

Теги:
multithreading
file-io
round-robin
job-scheduling

3 ответа

2
Лучший ответ

Пожалуйста, см. Также ответ Джеймса. Он указывает на критическую ошибку, которая ускользнула от моего уведомления: вы закрываете файл до того, как потоки писателя закончили. Подумайте о публикации нового вопроса, чтобы спросить, как решить эту проблему, так как этот "вопрос" - это уже три вопроса, которые перевернуты в один.

  1. FileShare.Write сообщает операционной системе разрешить другим попыткам открыть файл для записи. Обычно это используется для систем, в которых несколько процессов записываются в один и тот же файл. В вашем случае у вас есть один процесс, и он открывает файл один раз, поэтому этот флаг не имеет никакого значения. Это неправильный инструмент для работы.

Чтобы скопировать записи между несколькими потоками, вы должны использовать блокировку. Добавить новое статическое поле в класс:

private static object synchronizer = new object();

Затем заверните каждую операцию записи в файл с помощью блокировки на этом объекте:

lock(synchronizer)
{
    Console.WriteLine("[Thread1] " + i);    
}

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

Говоря о том, что переключение с файла на запись на консоль записывается в сторону, проблема с файлом была умной идеей, так что это было для этого. Насколько бы более реалистичная реализация этой идеи заключалась бы в замене всех вызовов записи вызовом одной функции, например "WriteOutput (string)", чтобы вы могли переключать все из файла на консоль, просто изменив одну строку в этой функции,

И тогда вы также можете поместить замок в эту функцию.

  1. Резьбовое вещество не детерминировано. Он гарантировал, что каждый поток будет запущен, но нет никаких гарантий относительно порядка, когда потоки будут прерваны, какой поток будет прерывать, и т.д. Это рулон кости каждый раз. Вам просто нужно привыкнуть к этому или уйти с пути, чтобы заставить вас произойти в определенной последовательности, если это действительно важно для вашего приложения.

  2. Я не знаю об этом. Похоже, это не имеет значения.

  • 0
    Сэр, есть ли способ убедиться, что другие потоки неактивны, пока один поток записывает в файл; и, соответственно, предотвращение прерывания, которое происходит?
  • 0
    Это то, что делает блокировка. После того, как поток получает блокировку, следующий поток (или потоки), которые пытаются получить блокировку, будут ждать, пока блокировка не будет снята (что происходит, когда поток, получивший блокировку, выходит из оператора "lock {}"). Таким образом, если все ваши записи находятся внутри «lock (synchronizer)», тогда только один поток сможет писать в любой момент.
Показать ещё 1 комментарий
4
  1. Ваша основная функция заключается в завершении и закрытии файла до того, как потоки начали писать на него, поэтому вы можете использовать Thread.Join, чтобы ждать выхода потока. Также я бы посоветовал using инструкцию using для объектов IDisposable.

  2. Когда у вас ограниченные ресурсы, которые вы хотите разделить между потоками, вам понадобится механизм блокировки. Планирование потоков не является детерминированным. Вы запустили 5 потоков, и в этот момент он не гарантировал, что он будет запускаться первым. lock заставит поток ждать, пока ресурс станет бесплатным. Порядок все еще не определен, поэтому T3 может работать до T2, если вы не добавите дополнительную логику/блокировку, чтобы заставить порядок также.

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

В качестве дополнительной заметки я бы избежал использования Sleep как способа синхронизации потоков.

Чтобы эффективно получать один поток для записи за один раз, вам нужно заблокировать все остальные потоки, есть несколько методов для этого, таких как lock, Mutex, Monitor, AutoResetEvent и т.д. Я бы использовал AutoResetEvent для этой ситуации. Проблема, с которой вы сталкиваетесь, - это каждый поток, который должен знать, какой поток он ожидает, чтобы он мог ждать правильного события.

0

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню