@YaMirok

Почему не получается синхронизировать асинхронный метод?

Добрый день всем!
Передо мной стоит следующая задача. Необходимо выдать сдачу монетами пользователю через устройства выдачи. Проблема заключается в том, что устройства могут затупить и не выдать всю сумму. Для этого я планировал использовать следующий алгоритм:
1) подать команду на выдачу N монет
2) получить асинхронный ответ о выдачи N монет (есть соответствующий callback #1) или получить сообщение об ошибке с кодом и текстом (уже другой callback #2);
3) запросить общее число выданных монет (тут он(хоппер) врать не может) и пишет сколько выдал - M. Результат также приходит асинхронно в соответствующий callback #3. Необходима ждать ответа.
4) Сравниваю M и N, если не равны переходим к п.1, но заменив N на N-M, если равны идем к следующему хопперу или на экран с результатом.

Итак, сама суть проблемы.
В моем классе COHopper есть следующий метод:
internal bool DispenseCoins(int numberOfCoins)
        {
            if (!IsInited) return false;
            var methodName = MethodBase.GetCurrentMethod().Name;
            Logger.Fatal("Вызван {0} c параметром numberOfCoins={1}", methodName, numberOfCoins);
            try
            {
                var loop = true;
                var coinsCount = numberOfCoins;
                if(coinsCount==0)return true;
                while (loop)
                {
                    if (DispenseCoinsWithCount(coinsCount))
                    {
                        Logger.Trace("Перед SendDispenseCoinsSendDispenseCoins");
                        while (SendDispenseCoins)
                        {
                            Thread.Sleep(100);
                        }
                        Logger.Trace("После coinsToGivePair.Key.SendDispenseCoins");
                    }
                    else Logger.Trace("DispenseCoinsWithCount - {0}", "False");
                    if (GetInfoAboutAllCountOfCoins())
                    {
                        Logger.Trace("Перед SendDispenseAllCoins");
                        while (SendDispenseAllCoins)
                        {
                            Thread.Sleep(100);
                        }
                        Logger.Trace("После SendDispenseAllCoins");
                    }
                    else Logger.Trace("GetInfoAboutAllCountOfCoins - {0}", "False");
                    if (LastCoinsDeliveryInfo == numberOfCoins)
                        loop = false;
                    coinsCount = numberOfCoins - LastCoinsDeliveryInfo;
                }
                return true;
            }
            catch (Exception e)
            {
                Logger.Fatal("{0} - {1}:{2}", methodName, MessageResource.ErrorUnExpectedException, e.Message);
                return false;
            }
        }


Где флаги SendDispenseAllCoins и SendDispenseCoins меняются в соответствующих callback`ах:
SendDispenseCoins - либо по приходу сообщения об успешной выдачи в callback #1, либо по приходу сообщения об ошибке в callback #2
SendDispenseAllCoins - по приходу сообщения в callback #3

В результате происходит зацикливание на первом while. Хотя сообщения приходят и значения меняются.
Я считаю, что данный способ ожидания сообщений несколько туповат. Может у кого-нибудь есть опыт написания подобных вещей, буду признателен, если поделитесь. Или подскажете как исправить данную реализацию.
  • Вопрос задан
  • 2631 просмотр
Решения вопроса 1
xakpc
@xakpc
full-stack .net developer, CEO Leecero.com
Если мы говорим о многопоточности для синхронизации потоков используйте объекты синхронизации (например ManualResetEvent). Тогда не надо городить всю эту идею с callback'ами
и вот этот убогий код
while (SendDispenseAllCoins)
{
    Thread.Sleep(100);
}

можно заменить 1 строчкой, например такой
mre.WaitOne(TimeSpan.FromMinutes(1))

Это в случае ручного создания потоков.
Но если у вас .net 4.5 рекомендую не заниматься ручным созданием потоков а использовать "Асинхронный шаблон, основанный на задачах" (TAP), а именно конструкции async-await и Task'и
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Замените
if (LastCoinsDeliveryInfo == numberOfCoins)
     loop = false;
coinsCount = numberOfCoins - LastCoinsDeliveryInfo;

на
if (LastCoinsDeliveryInfo == coinsCount)
     loop = false;
coinsCount -= LastCoinsDeliveryInfo;

Ибо я подозреваю, что LastCoinsDeliveryInfo содержит лишь число монет, выданных в последний раз.

Но вообще, так асинхронный код не пишется. Thread.Sleep - это детский сад какой-то. Используйте лучше Future/Promise, или wait/notify, что там в C# есть. Помимо неэффективности, асинхронное изменение полей SendDispenseCoins и SendDispenseAllCoins может и не сработать: основной поток может просто не увидеть изменения, сделанные другим потоком.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы