SemaphoreSlim(1, 1) WaitAsync
От: #John Европа https://github.com/ichensky
Дата: 28.12.20 11:53
Оценка: 7 (1)
Здравствуйте,

почему в данном примере SemaphoreSlim(1, 1) с WaitAsync не работает как `lock`
программа выводит разный результат. (.net 5)

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static SemaphoreSlim s = new SemaphoreSlim(1, 1);

        const int operations = 10000;
        static int balace = 0;

        static async Task Main(string[] args)
        {
            var tasks = new List<Task>();
            for (int index = 0; index < 10; index++)
            {
                balace = 0;
                for (int i = 0; i < operations; i++)
                {
                    tasks.Add(Task.Factory.StartNew(add));
                }
                for (int i = 0; i < operations; i++)
                {
                    tasks.Add(Task.Factory.StartNew(remove));
                }

                await Task.WhenAll(tasks);
                Console.WriteLine(balace);
            }
            Console.ReadLine();
        }

        static async Task add()
        {
            await s.WaitAsync();
            try
            {
                balace++;
                await Task.Yield();
            }
            finally
            {
                s.Release();
            }
        }
        static async Task remove()
        {
            await s.WaitAsync();
            try
            {
                balace--;
                await Task.Yield();
            }
            finally
            {
                s.Release();
            }
        }

    }
}
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re: SemaphoreSlim(1, 1) WaitAsync
От: gabryael  
Дата: 28.12.20 13:17
Оценка: 10 (1)
Здравствуйте, #John, Вы писали:

tasks.Add(await Task.Factory.StartNew(add));


или
tasks.Add(Task.Factory.StartNew(add).Unwrap());


или просто
tasks.Add(Task.Run(add));


то же самое для remove.
Re[2]: SemaphoreSlim(1, 1) WaitAsync
От: #John Европа https://github.com/ichensky
Дата: 28.12.20 15:26
Оценка:
Здравствуйте, gabryael, Вы писали:

G>
G>tasks.Add(await Task.Factory.StartNew(add));
G>


Такой вариант не подходит, т.к. новые таски будут создаваться и завершаться последовательно.

G>или

G>
G>tasks.Add(Task.Factory.StartNew(add).Unwrap());
G>


Так не работает.

G>или просто

G>
G>tasks.Add(Task.Run(add));
G>


Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[3]: SemaphoreSlim(1, 1) WaitAsync
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 28.12.20 15:31
Оценка:
Здравствуйте, #John, Вы писали:

J>Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?


balace-- атомарная. Даже если lock не работает ты должен получить 0.

Но вот все ли задачи запустились? Проверь общее колчество balace-- и balace++
и солнце б утром не вставало, когда бы не было меня
Re[3]: SemaphoreSlim(1, 1) WaitAsync
От: gabryael  
Дата: 28.12.20 16:22
Оценка: 10 (1) +1
Здравствуйте, #John, Вы писали:

J>Здравствуйте, gabryael, Вы писали:


G>>
G>>tasks.Add(await Task.Factory.StartNew(add));
G>>


J>Такой вариант не подходит, т.к. новые таски будут создаваться и завершаться последовательно.


Это не так.

G>>или

G>>
G>>tasks.Add(Task.Factory.StartNew(add).Unwrap());
G>>


J>Так не работает.


Работает.

G>>или просто

G>>
G>>tasks.Add(Task.Run(add));
G>>


J>Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?


SemaphoreSlim работает как должен. Просто ты ожидаешь не те таски. Task.Factory.StartNew(...) возвращает Task<Task> для асинхронных делегатов, поэтому нужен еще один await или Unwrap(). Подробнее
Re[3]: SemaphoreSlim(1, 1) WaitAsync
От: ksg71 Германия  
Дата: 28.12.20 16:25
Оценка: 24 (2)
Здравствуйте, #John, Вы писали:

J>Здравствуйте, gabryael, Вы писали:


G>>
G>>tasks.Add(await Task.Factory.StartNew(add));
G>>


J>Такой вариант не подходит, т.к. новые таски будут создаваться и завершаться последовательно.


G>>или

G>>
G>>tasks.Add(Task.Factory.StartNew(add).Unwrap());
G>>


J>Так не работает.


G>>или просто

G>>
G>>tasks.Add(Task.Run(add));
G>>


J>Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?


https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

в части

Does not understand async delegates. This is actually the same as point 1 in the reasons why you would want to use StartNew. The problem is that when you pass an async delegate to StartNew, it’s natural to assume that the returned task represents that delegate. However, since StartNew does not understand async delegates, what that task actually represents is just the beginning of that delegate. This is one of the first pitfalls that coders encounter when using StartNew in async code.


и await Task.WhenAll(tasks); поэтому "преждевременно" завершается
сам s.WaitAsync работает как должен,
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[4]: SemaphoreSlim(1, 1) WaitAsync
От: #John Европа https://github.com/ichensky
Дата: 28.12.20 16:26
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, #John, Вы писали:


J>>Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?


S>balace-- атомарная. Даже если lock не работает ты должен получить 0.


balace-- — не атомарная операция.

S>Но вот все ли задачи запустились? Проверь общее колчество balace-- и balace++


Да, похоже в этом была ошибка.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[4]: SemaphoreSlim(1, 1) WaitAsync
От: Ночной Смотрящий Россия  
Дата: 28.12.20 16:40
Оценка:
Здравствуйте, Serginio1, Вы писали:

J>>Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?

S>balace-- атомарная.

Нет, операция декремента не атомарная.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: SemaphoreSlim(1, 1) WaitAsync
От: karbofos42 Россия  
Дата: 28.12.20 16:42
Оценка:
Здравствуйте, gabryael, Вы писали:

G>
G>tasks.Add(await Task.Factory.StartNew(add));
G>


А назвали бы в Майкрософте метод как положено Task.Factory.StartNewAsync, глядишь и темы этой не было бы.
Я сам уже думал, что чудеса какие-то и в глаза это не бросилось, т.к. обычно через Task.Run запускаю задачи )
Re[5]: SemaphoreSlim(1, 1) WaitAsync
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 28.12.20 16:46
Оценка:
Здравствуйте, #John, Вы писали:


S>>balace-- атомарная. Даже если lock не работает ты должен получить 0.


J>balace-- — не атомарная операция.


Да тупанул. Просто запись атомарна!
и солнце б утром не вставало, когда бы не было меня
Отредактировано 28.12.2020 16:56 Serginio1 . Предыдущая версия .
Re[5]: SemaphoreSlim(1, 1) WaitAsync
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 28.12.20 16:56
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Здравствуйте, Serginio1, Вы писали:


J>>>Так работает. Почему в случаи `tasks.Add(Task.Factory.StartNew())` SemaphoreSlim(1,1) WaitAsync не работает как lock?

S>>balace-- атомарная.

НС>Нет, операция декремента не атомарная.

Да тупанул. Просто запись атомарна
и солнце б утром не вставало, когда бы не было меня
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.