Semaphore в TPL

202
03 августа 2018, 15:30

В моей программе доступ к функции должно иметь только определенное количество потоков. Есть следующий код:

int totalTasksExecute = 3, currentTasksExecute = 0;
Task[] tasks = new Task[totalTasksExecute];
foreach(ObjModel obj in Objs)
{
    if (currentTasksExecute == totalTasksExecute)
    {
        Task.WaitAll(tasks);
        currentTasksExecute = 0;
    }
    Thread.Sleep(500);
    tasks[currentTasksExecute++] = Task.Factory.StartNew(() => GetWebInfo(obj));
}
Task.WaitAll(tasks);

Так как он встречается несколько раз, то я решил оформить в виде отдельного класса. В классе для одновременного ограничения доступа к функции я использую Semaphore.

public class MultiTasks<T>
{
    private readonly Action<T> _callback;
    private readonly Semaphore semaphore;
    private readonly List<T> ts;
    private readonly AutoResetEvent[] waitHandles;
    private int Count;
    public MultiTasks(Action<T> callback, int maxRunTasks, List<T> objListForTask)
    {
        ts = objListForTask;
        _callback = callback;
        semaphore = new Semaphore(maxRunTasks, maxRunTasks);
        waitHandles = new AutoResetEvent[objListForTask.Count];
        for (int i = 0; i < waitHandles.Length; i++)
            waitHandles[i] = new AutoResetEvent(false);
    }
    public void Start(int waitMilliSeconds)
    {
        Count = 0;
        foreach(T obj in ts)
            Task.Factory.StartNew(() =>
            {
                semaphore.WaitOne();
                _callback(obj);
                waitHandles[Count].Set();
                Interlocked.Increment(ref Count);
                Thread.Sleep(waitMilliSeconds);
                semaphore.Release();
            });
        WaitHandle.WaitAll(waitHandles);
    }
}

Вызываю таким образом:

MultiTasks<ObjModel> multiTasks = 
   new MultiTasks<ObjModel>(GetWebInfo, 3, objs.ToList());
multiTasks.Start(500);

Вопрос в том, что в первом случае время обработки занимает около 30 сек, то во втором случае в 2 раза больше. Может быть класс не правильно реализовал? Или не правильно использую Semaphore?

Answer 1

Если вы делаете что то, что должно работать параллельно, но не более N потоков одновременно, может, имеет смысл пользоваться готовыми конструкциями? Как пример:

int i = 0;
Enumerable.Range(1, 100).AsParallel().WithDegreeOfParallelism(3).ForAll(x =>
{
    Interlocked.Increment(ref i);
    Console.WriteLine($"Processing {x} - threads there - {i}");
    Interlocked.Decrement(ref i);
});

Вывод будет что то типа

Processing 1 - threads there - 1
Processing 2 - threads there - 1
Processing 4 - threads there - 3
Processing 6 - threads there - 3
Processing 7 - threads there - 3
Processing 8 - threads there - 3
Processing 9 - threads there - 3
.........
Processing 93 - threads there - 3
Processing 94 - threads there - 3
Processing 95 - threads there - 3
Processing 96 - threads there - 3
Processing 97 - threads there - 3
Processing 98 - threads there - 3
Processing 99 - threads there - 3
Processing 100 - threads there - 3
Processing 19 - threads there - 3
Processing 20 - threads there - 2
Processing 5 - threads there - 3

То есть не более 3 одновременно запущенных потока.

READ ALSO
Key Hook не определяет 4 кнопки сразу

Key Hook не определяет 4 кнопки сразу

Хочу выводить нажатые клавиши пользователем, в том числе сочетанияПробовал разные Key Lisener/Hook (они работают по одному и тому же принципу), но везде...

195
Обойти дерево в глубину

Обойти дерево в глубину

Есть такой набор данных:

205
Excel выбор листа при импорте laravel

Excel выбор листа при импорте laravel

Как сделал выборку листов при импорте из Excel файла в бд laravelНа данный момент мой импорт:

195
Doctrine не подтягивать все связи

Doctrine не подтягивать все связи

Например, когда я пишу так: $users = $this->getDoctrine()->getRepository('ProfileBundle:User')->findBy(['deletedAt' => null]); то к модели пользователя подтягиваются все связанные...

151