namespace WebReader
{
public partial class frm_Main : Form
{
public frm_Main()
{
InitializeComponent();
}
private void frm_Main_Load(object sender, EventArgs e)
{
using var clt= new HttpClient();
var res = clt.GetAsync("https://daera.net/dmoon/testfile.txt");
while (true)
{
if (res.IsCompletedSuccessfully)
{
var msg = clt.GetStringAsync("https://daera.net/dmoon/testfile.txt");
MessageBox.Show(msg.ToString());
break;
}
}
}
}
}
Предполагается, что этот код отображает содержимое текстового файла https://daera.net/dmoon/testfile.txt
, но он показывает окно сообщения с некоторой технической информацией об объекте clt и ничего больше.
Что я делаю неправильно? Я использую Visual Studio 2019.
Вам также нужен только звонок httpClient.GetStringAsync()
, тот, который вы сделали раньше, является избыточным.
Вызов GetStringAsync()
также является асинхронным и возвращает Task<String>
. Вы могли бы сделать с ним что-то похожее на то, что вы сделали с возвращением GetAsync()
. В любом случае, я бы заменил цикл while (true)
вызовом Task.Wait()
. Также см. метод ContinueWith()
.
Упрощаем, но вам нужны await
результаты асинхронных методов:
using var clt= new HttpClient();
var res = await clt.GetAsync("https://daera.net/dmoon/testfile.txt");
Сделайте это, и петля while (true)
вам не понадобится.
Хорошо, но поскольку frm_Main_Load
, в котором находится этот код, не отмечен async
, то await
будет невозможно - нет?
Это верно. Ключевое слово async «позволяет использовать ожидание». В любом случае, ваша текущая проблема не в том, как вы синхронизируете; дело в том, что вы не синхронизируете вторую задачу. Проверьте мой ответ.
В результате появляется сообщение об ошибке: CS4033 Оператор ожидания можно использовать только внутри асинхронного метода. Рассмотрите возможность пометки этого метода модификатором async и изменения типа возвращаемого значения на Task.
@YoustayIgo, так сделай это: отметьте метод «async».
@JoelCoehoorn Как это сделать? Я новичок в C#. Я знаю VB6 и C++, но я совсем новичок в Visual Studio и C#. Извините, что я такой зеленый.
Как я уже говорил в комментариях, вызов GetStringAsync()
также асинхронен и возвращает Task<String>
.
У вас есть несколько вариантов синхронизации этих задач с вашей основной программой.
while(true)
, проверяющий IsCompletedSuccessfully
. Это вообще не рекомендуется, потому что это съедает ваши ресурсы, ничего не делая.Task.Wait()
. Это блокирующий вызов и то, что вы ищете. (Отредактировано: он ближе к исходному циклу без потребления ресурсов; но вы, вероятно, не хотите блокировать свою форму до тех пор, пока не завершится HTTP-вызов, что приведет к тому, что ваша программа перестанет отвечать на запросы. Найдите вариант 3 или Task.ContinueWith()
)await
, как в ответе @Joel (*).Это ваш код, использующий вариант 1 для GetAsync()
(опять же, не рекомендуется) и вариант 2 для GetStringAsync()
.
namespace WebReader
{
public partial class Program
{
static void Main ()
{
using var clt= new HttpClient();
var res = clt.GetAsync("https://daera.net/dmoon/testfile.txt");
while (true)
{
if (res.IsCompletedSuccessfully)
{
var responseTask = clt.GetStringAsync("https://daera.net/dmoon/testfile.txt");
responseTask.Wait();
Console.WriteLine(responseTask.Result.ToString());
break;
}
}
}
}
}
(*) Более технический. Асинхронные функции выполняются в отдельной задаче (представьте это как отдельный поток или облегченный процесс, но несколько задач могут фактически повторно использовать один и тот же поток). Когда вы используете await
для вызова асинхронной функции B из функции A, вызывающая сторона (A) не будет выполнять инструкции, следующие за вызовом, пока асинхронная задача (и вызываемая сторона B) не завершится; но A немедленно вернется к вызывающему абоненту. То есть вызывающая сторона (A) также будет асинхронной (и это причина обязательного ключевого слова async
). Если A — ваша функция main(), она может вернуться в среду выполнения .NET (цикл Windows Forms и т. д.), пока асинхронные задачи все еще выполняются.
Отредактировано. Как уже было сказано, использовать await
проще, если вы можете пометить свою функцию Main()
как async
. Я не знаю, сможете ли вы сделать это с вашей версией Windows Forms (читайте комментарий @Mixin), но вы можете взять этот код консольного приложения (который работает) в качестве примера:
namespace WebReader
{
public partial class Program
{
public static async Task Main ()
{
using var clt = new HttpClient();
var response = await clt.GetAsync("https://daera.net/dmoon/testfile.txt");
// You should check response.StatusCode here
var responseContent = await clt.GetStringAsync("https://daera.net/dmoon/testfile.txt");
Console.WriteLine(responseContent.ToString());
}
}
}
Как вариант, вы можете написать точно такой же код в отдельной функции, как эта, и вызывать эту функцию из фактического frm_Main()
, используя Task.Wait(yourAsyncFunction())
.
Это работает, да. Но как сделать это эффективно с помощью async и await?
@YoustayIgo Используйте private async void frm_Main_Load(object sender, EventArgs e)
Я отредактировал свой ответ, включив в него пример техники await
. Это по-прежнему консольное приложение, но суть вы поняли.
@MinxinYu-MSFT Это работает. Спасибо, что покормили новичка с ложечки. Весьма признателен. Отметил этот ответ как принятый.
Вы не ожидаете звонков, поэтому объекты, которые вы используете, просто
Task<T>
и пока не содержат ничего действенного.