19 / 19 / 14
Регистрация: 06.08.2009
Сообщений: 533
1

Асинхронный метод долго (никогда?) не завершается

03.07.2018, 08:49. Показов 1469. Ответов 7
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Добрый день.
Вызываю асинхронный метод вот так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
            StockOrderInfo info;
            info.account = "";
            info.count = 28;
            info.expiry = DateTime.Now.AddYears(1);
            info.market = "";
            info.ticker = "ADXBTC";
            info.type = "buy";
            info.price= 0.00005m;
            info.signalPrice = 0;
            info.stopType = "";
            Task<ResultSendOrder> rsi_task=driver.sendOrder(info);
 
            //Ждем выполнения ассинхронного метода
            DateTime now = DateTime.Now;
            TimeSpan time_out = new TimeSpan(0, 0, 10);
            while ((!rsi_task.IsCompleted) && (!rsi_task.IsFaulted))
            {
 
                if (DateTime.Now - now > time_out)
                {
                    MessageBox.Show("Таймаут");
                    return;
                }
                Thread.Sleep(100);
            }
 
            ResultSendOrder sor = rsi_task.GetAwaiter().GetResult();
 
            MessageBox.Show(sor.resCode.ToString()+". "+sor.resMessage);
И в результате цикл почему-то всегда завершается по таймауту, хотя, 10 секунд, вроде как должно быть достаточно.
А делает этот асинхронный метод вот что:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        /// <summary>
        /// Выстаивть ордер
        /// </summary>
        /// <param name="info">Информация о выставляемом оредере</param>
        /// <returns>Результат выставления ордера</returns>
        public async Task<ResultSendOrder> sendOrder(StockOrderInfo info)
        {
            
            //res.time_beg = DateTime.Now;
            Task<Binance.API.Csharp.Client.Models.Account.NewOrder> result_task = null;
            OrderSide order_side = OrderSide.BUY;
            if (info.type.ToLower() == "sell") order_side = OrderSide.SELL;
            loader.load();
            binanceClient.unix_time = loader.unix_time;
            if (info.price == 0m)
            {
                result_task = binanceClient.PostNewOrder(info.ticker, info.count, info.price, order_side, Binance.API.Csharp.Client.Models.Enums.OrderType.MARKET);
            }
            else
            {
                result_task = binanceClient.PostNewOrder(info.ticker, info.count, info.price, order_side);
            }
 
            return await Task<ResultSendOrder>.Run(() =>
            {
                ResultSendOrder res;
                res.time_beg = DateTime.Now;
                while(!result_task.IsCompleted && !result_task.IsFaulted) Thread.Sleep(100);
                Binance.API.Csharp.Client.Models.Account.NewOrder result = result_task.GetAwaiter().GetResult();
                res.date_time = DateTime.Now;
                res.fee = 0;
                res.orderCode = result.ClientOrderId;
                res.price = 0;
                res.resCode = 0;
                res.resMessage = result.ClientOrderId + " " + result.OrderId + " " + result.Symbol + " "+result.TransactTime;
                res.time_end = DateTime.Now;
                return res;
            });
 
        }
где PostNewOrder делает обращение к серверу биржи:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        public async Task<NewOrder> PostNewOrder(string symbol, decimal quantity, decimal price, OrderSide side, OrderType orderType = OrderType.LIMIT, TimeInForce timeInForce = TimeInForce.GTC, decimal icebergQty = 0m, long recvWindow = 5000)
        {
            //Validates that the order is valid.
            ValidateOrderValue(symbol, orderType, price, quantity, icebergQty);
 
            string s_price = price.ToString().Replace(",", ".");
 
            var args = $"symbol={symbol.ToUpper()}&side={side}&type={orderType}&quantity={quantity}"
                + (orderType == OrderType.LIMIT ? $"&timeInForce={timeInForce}" : "")
                + (orderType == OrderType.LIMIT ? $"&price={s_price}" : "")
                + (icebergQty > 0m ? $"&icebergQty={icebergQty}" : "")
                + $"&recvWindow={recvWindow}";
            var result = await _apiClient.CallAsync<NewOrder>(ApiMethod.POST, EndPoints.NewOrder, true, args,unix_time);
 
            return result;
        }
Самое интересное, что программа делает что надо - заявка реально выставляется на бирже, но Task почему-то не хочет завершается, подскажите пожалуйста, что делаю не так?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
03.07.2018, 08:49
Ответы с готовыми решениями:

Почему Task никогда не завершается?
Добрый день! Пробую свои силы в async/await. Подскажите, почему в данном коде никогда не...

Морской бой: Проверка на победителя никогда не завершается
Здравствуйте дорогие форумчане. Есть у меня некий код морского боя, не полный. Проблема у меня...

Работа компьютера очень долго завершается
ОС: Windows 7 x64 Максимальная Компьютер - член домена. Проблема в том, что работа компьютера...

Не завершается метод
По идее оператор return; должен завершить метод Turning(). Но через брейкпоинты я посмотрел что...

7
Эксперт .NET
17685 / 12871 / 3365
Регистрация: 17.09.2011
Сообщений: 21,136
03.07.2018, 09:47 2
Цитата Сообщение от megabax Посмотреть сообщение
Task почему-то не хочет завершается, подскажите пожалуйста, что делаю не так?
Совершаете самую распространенную ошибку при работе с асинхронными методами: мешаете асинхронные вызовы с синхронным ожданием, усугубляя все это вызовами Thread.Sleep.

Кстати, зачем каждый раз вы вызываете GetAwaiter на задаче? Почему не обращаетесь сразу к результату?
0
19 / 19 / 14
Регистрация: 06.08.2009
Сообщений: 533
03.07.2018, 10:17  [ТС] 3
Цитата Сообщение от kolorotur Посмотреть сообщение
Кстати, зачем каждый раз вы вызываете GetAwaiter на задаче? Почему не обращаетесь сразу к результату?
А что, так можно??

Переделал вот так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
            Task<ResultSendOrder> rsi_task=driver.sendOrder(info);
 
            //Ждем выполнения ассинхронного метода
            DateTime now = DateTime.Now;
            TimeSpan time_out = new TimeSpan(0, 0, 10);
 
            bool is_complete = false;
 
            var awaiter = rsi_task.GetAwaiter();
            awaiter.OnCompleted(() =>
            {
                is_complete = true;
                //var result = awaiter.GetResult();
                ResultSendOrder sor1 = awaiter.GetResult();
                listBox1.Items.Add(sor1.resCode.ToString() + ". " + sor1.resMessage);
                //MessageBox.Show(sor1.resCode.ToString() + ". " + sor1.resMessage);
            }
            );
Что-то вроде заработало. Но не нравится как у меня сделан метод sendOrder:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        /// <summary>
        /// Выстаивть ордер
        /// </summary>
        /// <param name="info">Информация о выставляемом оредере</param>
        /// <returns>Результат выставления ордера</returns>
        public async Task<ResultSendOrder> sendOrder(StockOrderInfo info)
        {
            
            //res.time_beg = DateTime.Now;
            Task<Binance.API.Csharp.Client.Models.Account.NewOrder> result_task = null;
            OrderSide order_side = OrderSide.BUY;
            if (info.type.ToLower() == "sell") order_side = OrderSide.SELL;
            loader.load();
            binanceClient.unix_time = loader.unix_time;
            if (info.price == 0m)
            {
                result_task = binanceClient.PostNewOrder(info.ticker, info.count, info.price, order_side, Binance.API.Csharp.Client.Models.Enums.OrderType.MARKET);
            }
            else
            {
                result_task = binanceClient.PostNewOrder(info.ticker, info.count, info.price, order_side);
            }
 
            return await Task<ResultSendOrder>.Run(() =>
            {
                ResultSendOrder res;
                res.time_beg = DateTime.Now;
 
                /*var awaiter = result_task.GetAwaiter();
                awaiter.OnCompleted(() =>
                {
                    Binance.API.Csharp.Client.Models.Account.NewOrder result = awaiter.GetResult();
                    res.date_time = DateTime.Now;
                    res.fee = 0;
                    res.orderCode = result.ClientOrderId;
                    res.price = 0;
                    res.resCode = 0;
                    res.resMessage = result.ClientOrderId + " " + result.OrderId + " " + result.Symbol + " " + result.TransactTime;
                    res.time_end = DateTime.Now;
                }
                );*/
 
                while (!result_task.IsCompleted && !result_task.IsFaulted) Thread.Sleep(100);
                Binance.API.Csharp.Client.Models.Account.NewOrder result1 = result_task.GetAwaiter().GetResult();
                res.date_time = DateTime.Now;
                res.fee = 0;
                res.orderCode = result1.ClientOrderId;
                res.price = 0;
                res.resCode = 0;
                res.resMessage = result1.ClientOrderId + " " + result1.OrderId + " " + result1.Symbol + " "+ result1.TransactTime;
                res.time_end = DateTime.Now;
                return res;
            });
 
        }
Но что-то не совсем понимаю, как мне из обработчика OnCompleted авэйтера извлечь данные для того, чтобы их могла возвратить функция (метод).

Вообще, задача состоит в следующем, чтобы то, что вернула библиотека в своем формате, конвертировать в мой формат, который совместим с моим кодом, поддерживающим некий универсальный интерфейс ITerminalDriver.
0
Эксперт .NET
17685 / 12871 / 3365
Регистрация: 17.09.2011
Сообщений: 21,136
03.07.2018, 10:34 4
Лучший ответ Сообщение было отмечено kolorotur как решение

Решение

Цитата Сообщение от megabax Посмотреть сообщение
Переделал вот так
И опять там GetAwaiter. Зачем он там?

Цитата Сообщение от megabax Посмотреть сообщение
Вообще, задача состоит в следующем, чтобы то, что вернула библиотека в своем формате, конвертировать в мой формат, который совместим с моим кодом, поддерживающим некий универсальный интерфейс ITerminalDriver.
Как выглядит интерфейс?

Добавлено через 15 минут
Попробуйте переписать так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        public async Task<NewOrder> PostNewOrder(string symbol, decimal quantity, decimal price, OrderSide side, OrderType orderType = OrderType.LIMIT, TimeInForce timeInForce = TimeInForce.GTC, decimal icebergQty = 0m, long recvWindow = 5000)
        {
            //Validates that the order is valid.
            ValidateOrderValue(symbol, orderType, price, quantity, icebergQty);
 
            string s_price = price.ToString().Replace(",", ".");
 
            var args = $"symbol={symbol.ToUpper()}&side={side}&type={orderType}&quantity={quantity}"
                + (orderType == OrderType.LIMIT ? $"&timeInForce={timeInForce}" : "")
                + (orderType == OrderType.LIMIT ? $"&price={s_price}" : "")
                + (icebergQty > 0m ? $"&icebergQty={icebergQty}" : "")
                + $"&recvWindow={recvWindow}";
 
            // ConfigureAwait здесь не обязателен, но лучше его поставить, т.к. контекст синхронизации в данном случае не важен.
            var result = await _apiClient.CallAsync<NewOrder>(ApiMethod.POST, EndPoints.NewOrder, true, args,unix_time).ConfigureAwait(false);
 
            return result;
        }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
        /// <summary>
        /// Выстаивть ордер
        /// </summary>
        /// <param name="info">Информация о выставляемом оредере</param>
        /// <returns>Результат выставления ордера</returns>
        public async Task<ResultSendOrder> sendOrder(StockOrderInfo info)
        {
            
            //res.time_beg = DateTime.Now;
            Task<Binance.API.Csharp.Client.Models.Account.NewOrder> result_task = null;
            OrderSide order_side = OrderSide.BUY;
            if (info.type.ToLower() == "sell") order_side = OrderSide.SELL;
            loader.load();
            binanceClient.unix_time = loader.unix_time;
            if (info.price == 0m)
            {
                result_task = binanceClient.PostNewOrder(info.ticker, info.count, info.price, order_side, Binance.API.Csharp.Client.Models.Enums.OrderType.MARKET);
            }
            else
            {
                result_task = binanceClient.PostNewOrder(info.ticker, info.count, info.price, order_side);
            }
 
            var result = await result_task.ConfigureAwait(false);
            var now = DateTime.Now;
            var res = new ResultSendOrder
            {
               time_beg = now, 
               date_time = now,
               fee = 0,
               orderCode = result.ClientOrderId,
               price = 0,
               resCode = 0,
               resMessage = $"{result.ClientOrderId} {result.OrderId} {result.Symbol} {result.TransactTime}",
               time_end = now,
            }
            return res;
        }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
            StockOrderInfo info;
            info.account = "";
            info.count = 28;
            info.expiry = DateTime.Now.AddYears(1);
            info.market = "";
            info.ticker = "ADXBTC";
            info.type = "buy";
            info.price= 0.00005m;
            info.signalPrice = 0;
            info.stopType = "";
 
            var rsi_task = driver.sendOrder(info);
            var timeout = Task.Delay(TimeSpan.FromSeconds(10));
            var completedTask = await Task.WhenAny(rsi_task, timeout);
 
            if (completedTask == timeout)
            {
               MessageBox.Show("Таймаут");
               return;
            }
 
            var sor = await rsi_task;
            MessageBox.Show(sor.resCode.ToString()+". "+sor.resMessage);
Может что-то где-то пропустил, но суть в том, что если у вас есть асинхронные вызовы, то делайте их асинхронными, а не блокируйте потоки вызовами Thread.Sleep и GetResult.
1
19 / 19 / 14
Регистрация: 06.08.2009
Сообщений: 533
03.07.2018, 11:02  [ТС] 5
kolorotur, Спасибо.

Цитата Сообщение от kolorotur Посмотреть сообщение
Как выглядит интерфейс?
вот так:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    /// <summary>
    /// Интерфейс доступа к драйверу биржевого терминала
    /// </summary>
    public interface ITerminalDriver
    {
 
        /// <summary>
        /// Подсключиться к терминалу
        /// </summary>
        void connection();
 
        /// <summary>
        /// Отключиться от терминала
        /// </summary>
        void disconnection();
 
        
        /// <summary>
        /// Послать ордер (заявку)
        /// </summary>
        /// <param name="info">Структура заявки</param>
        /// <returns>Результат ввода заявки</returns>
        Task<ResultSendOrder> sendOrder(StockOrderInfo info);
 
        /// <summary>
        /// Количество данного инструмента на счете
        /// </summary>
        /// <param name="ticker">Тикер финансового инструмента</param>
        /// <returns>Количество данного инструмента на эмулируемом счете</returns>
        decimal itemCountOnAccount(string ticker);
 
        /// <summary>
        /// Количество доступных денежных средств
        /// </summary>
        /// <returns>Количество доступных денежных средств</returns>
        decimal moneyAvailable();
 
        /// <summary>
        /// Количество разновидностей финансовых инструментов, доступных для торгов
        /// </summary>
        /// <returns>Количество разновидностей финансовых инструментов, доступных для торгов</returns>
        int itemsCount();
 
        /// <summary>
        /// Количество заявок
        /// </summary>
        /// <returns>Количество заявок</returns>
        int ordersCount();
 
        /// <summary>
        /// Получить список заявок
        /// </summary>
        /// <returns>Список заявок</returns>
        List<Order> getOrders();
 
        /// <summary>
        /// Получить все открытые позиции
        /// </summary>
        /// <returns>Список открытых позиций</returns>
        List<StockItem> GetPositions();
 
        /// <summary>
        /// Получить стоимость портфеля
        /// </summary>
        /// <returns>Стоимость портфеля</returns>
        decimal getPortfolioPrice();
 
        /// <summary>
        /// Это вирутальный биржевой терминал
        /// </summary>
        /// <returns>true - виртулаьный, false - не виртуальный</returns>
        bool is_virtual();
 
    }
Он в процессе переписывания под асинхронные методы.
0
Эксперт .NET
17685 / 12871 / 3365
Регистрация: 17.09.2011
Сообщений: 21,136
03.07.2018, 11:13 6
Цитата Сообщение от megabax Посмотреть сообщение
Он в процессе переписывания под асинхронные методы.
Тем более нет причин использовать блокирующие вызовы.
В целом, если у вас в методе присутствует await, то использовать в том же методе или в вызывающих методах блокирующие вызовы (Thread.Sleep, GetAwaiter().GetResult, .Result и т.д.) — верный способ повесить поток.
Если этому потоку "посчастливиться" быть главным (UI), то наглухо повиснет все приложение.
0
19 / 19 / 14
Регистрация: 06.08.2009
Сообщений: 533
03.07.2018, 14:42  [ТС] 7
kolorotur, Спасибо. Можно еще вопрос? Как перехватить исключения в самом sendOrder, а не в этом коде:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
            Task<ResultSendOrder> rsi_task=driver.sendOrder(info);
 
            var timeout = Task.Delay(TimeSpan.FromSeconds(10));
            var completedTask = await Task.WhenAny(rsi_task, timeout);
 
            if (completedTask == timeout)
            {
                MessageBox.Show("Таймаут");
                return;
            }
 
            var sor = await rsi_task;
            MessageBox.Show(sor.resCode.ToString() + ". " + sor.resMessage);
То есть, допустим, бирже был послан неверный запрос. У меня по стандарту в этом случае sendOrder должен вернуть в resCode код ошибки, а не генерировать исключение.
Как вообще try ... catch в асинхроных методах работаю?

Добавлено через 10 минут
А все, спасибо, разобрался сам, сделал вот так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
            try
            {
                var result = await result_task.ConfigureAwait(false);
                var res = new ResultSendOrder
                {
                    time_beg = now,
                    date_time = now,
                    fee = 0,
                    orderCode = result.ClientOrderId,
                    price = 0,
                    resCode = 0,
                    resMessage = $"{result.ClientOrderId} {result.OrderId} {result.Symbol} {StockUtil.unix_date_time(result.TransactTime)}",
                    time_end = now,
                };
                return res;
            }
            catch(Exception ex)
            {
                var res = new ResultSendOrder
                {
                    time_beg = now,
                    date_time = now,
                    fee = 0,
                    orderCode = "0",
                    price = 0,
                    resCode = StockErrors.UnknownError,
                    resMessage = ex.Message,
                    time_end = now,
                };
                return res;
            }
0
Эксперт .NET
17685 / 12871 / 3365
Регистрация: 17.09.2011
Сообщений: 21,136
03.07.2018, 15:05 8
megabax, не повторяйтесь без нужды:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
string orderCode, message;
 
try
{
   var result = await result_task.ConfigureAwait(false);
   orderCode= result.ClientOrderId;
   message = $"{result.ClientOrderId} {result.OrderId} {result.Symbol} {StockUtil.unix_date_time(result.TransactTime)}";
}
catch (Exception ex)
{
   orderCode = "0"
   message = ex.Message;
}
 
var res = new ResultSendOrder
{
   time_beg = now,
   date_time = now,
   fee = 0,
   orderCode = orderCode,
   price = 0,
   resCode = 0,
   resMessage = message,
   time_end = now,
};
 
return res;
0
03.07.2018, 15:05
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
03.07.2018, 15:05
Помогаю со студенческими работами здесь

Асинхронный метод
Есть программа, архивирует видео-файлы с целью бэкапа, запускается каждый день в 20.00. Сейчас есть...

Фоновый/асинхронный метод
Моя программа делает следующее: программа делит файл на части, затем с созданными частями...

Асинхронный метод + потоки
Всем привет! Возможно вопрос и дилетантский, поэтому заранее извиняюсь. Делаю парсер сайта, на...

Сделать асинхронный метод с задержкой
Никак не могу разобраться с этими asynk\away если коротко, то есть два метода ПЕРВЫЙ должен...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru