1

Задается глобальный интервал (например, 0-1000). Есть метод write, который принимает интервал от 0 до 200, а также строку (обозначим ее S). Строка S генерируется в течение нескольких минут (генерирование идет в несколько потоков), сохраняет все строки в переменную list типа List<string>

Мне нужно в несколько потоков вызвать метод write, посылая ему интервал за интервалом (0-200, 200-400 и т.д.), а также новую строку S.

Я сделал так:

globalto = 1000;
globalfrom = 0;

double k =  (globalto - globalfrom) ;
k /= 200;
if (k > Math.Truncate(k))
    k = Math.Truncate(k) + 1;

long[] mas = new long[Convert.ToInt32(k)];
for (int i = 0; i < k; i++)
    mas[i] = globalfrom += 200;
mas.AsParallel().WithDegreeOfParallelism(thread).ForAll(j => write(j - 200, j, ""));

Но тут я не посылаю строку S. Не знаю как правильно реализовать, чтобы как только в переменной list появлялась строка, сразу создавался поток, который вызовет метод write, посылая текущую строку и новый отрезок, и чтобы всё это продолжалось до тех пор, пока не будет достигнут конец глобального интервала (0-1000).

Regent
  • 19,134
Lolidze
  • 1,370
  • Это случайно не producer/consumer у вас? – VladD Aug 01 '17 at 10:53
  • @VladD не совсем вас понял, но пусть будет ответ "нет" )) – Lolidze Aug 01 '17 at 10:57
  • @VladD можете подсказать, как все это дело организовать ? – Lolidze Aug 01 '17 at 11:00
  • @Lolidze, вам нужно отслеживать добавление элемента в коллекцию и выполнять при этом какие-то действия, так? – Андрей NOP Aug 01 '17 at 11:13
  • @Андрей да, как только в коллекцию добавилась строка, нужно создать поток, который вызовет метод и пошлет туда текущую строку и новый отрезок. ВАЖНО нужно чтоб все время брались новые отрезки, 0-200, 200-400, 400-600 и тд пока не дойдет до конца глобального отрезка, в данном случае 1000 – Lolidze Aug 01 '17 at 11:15
  • Коллекция обязательно нужна? Может можно вместо добавления элемента в коллекцию генерировать событие и передавать значение как параметр? – Андрей NOP Aug 01 '17 at 11:19
  • @Андрей хотелось бы все же с коллекцией сделать, чтоб генерация строк была отдельно и была возможность "запаса строк", например при втором запуске остались еще генерированные строки, которые не посылались в метод, и чтоб софт сразу с ними работал, а не ждал, пока генерируются новые строки. – Lolidze Aug 01 '17 at 11:24
  • @Андрей если с коллекцией никак нельзя, то придется сразу в методе генерации вызывать, но вот тут другая проблема, как послать новый отрезок, чтоб он не повторялся, ведь я в несколько потоков запускаю генерацию строки – Lolidze Aug 01 '17 at 11:25
  • Хорошо, это больше похоже на шаблон поставщик/потребитель, который выше упомянул @VladD. Вопрос тогда - вам принципиально надо быстро реагировать на новые элементы или можно потерпеть секунду-другую? – Андрей NOP Aug 01 '17 at 11:27
  • @Андрей принципиально. Но опять же, если не получается, то уже придется использовать другой способ – Lolidze Aug 01 '17 at 11:32

2 Answers2

2

Вариация Поставщик/Потребитель, применимая к вашей задаче

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main() => new Program().Run();

    Random Rnd = new Random();

    ConcurrentQueue<Interval> Items = new ConcurrentQueue<Interval>();

    int bigSegment = 2000;
    int smallSegment = 200;

    bool HasFinished = false;

    void Run()
    {
        // Запускаем потребителей
        var consumers = Enumerable.Range(0, 5)
                                  .Select(i => Task.Run(() => Consumer(i)))
                                  .ToArray();

        // Запускаем поставщиков
        var producers = Enumerable.Range(0, bigSegment / smallSegment)
                                  .Select(i => Task.Run(() => Producer(i)))
                                  .ToArray();
        // Ждем всех поставщиков
        Task.WaitAll(producers);
        // Ставим глобальный флаг
        HasFinished = true;
        // Ждем всех потребителей
        Task.WaitAll(consumers);
        Console.WriteLine("Работа завершена!");
        Console.ReadLine();
    }

    void Producer(int num)
    {
        Thread.Sleep(Rnd.Next(10) * 200); // Имитация продолжительной работы
        // Добавляем сгенерированный элемент в коллекцию
        Items.Enqueue(new Interval { Num = num, S = $"Строка {num}" });
        Console.WriteLine($"Поставщик {num} закончил работу");
    }

    void Consumer(int num)
    {
        while (!HasFinished)
        {
            while (!Items.IsEmpty)
            {
                // Пробуем извлечь элемент из коллекции
                if (Items.TryDequeue(out var item))
                    WriteAsync(item, num);
            }
            Thread.Sleep(100); // Спим 0,1 с
        }
        Console.WriteLine($"Потребитель {num} закончил работу");
    }

    void WriteAsync(Interval item, int num)
    {
        Thread.Sleep(Rnd.Next(10) * 100); // Имитация продолжительной работы
        Console.WriteLine($"Потребитель {num}: интревал {item.Num * smallSegment} - {(item.Num + 1) * smallSegment}, {item.S}");
    }
}

struct Interval
{
    public int Num { get; set; }
    public string S { get; set; }
}

Рекомендую изучить дополнительно ответы в этом вопросе: Имплементация Producer/Consumer pattern

1
namespace ConsoleApplication1
{
internal class Program
{
    static List<string> list= new List<string>();
    private static void Main(string[] args)
    {
        int globalto = 1000;
        int globalfrom = 0;
        int length = 200;

    for (int i=0; globalfrom +i*length<globalto;i++)
    {
        myFunc(globalfrom + i * length, Math.Min(globalfrom + (i+1) * length, globalto),i);
    }
    }

private static async void myFunc(int from, int to,int number)
    {
    while (list.Count <= number)
    {
        await Task.Delay(TimeSpan.FromMilliseconds(50));
    }
    write(from, to, list[string]);
    }
}
}