Creating/initializing 任务没有立即开始
Creating/initializing task without starting it immediately
我想从异步任务方法 create/initialize 任务对象而不直接启动它。在线搜索时,我只找到从 void 方法创建的任务的答案。
这是我要执行的任务方法。因为我需要做一些异步的网络请求,所以该方法需要是异步的。我也想做异常处理,所以它不能是 async void
.
private async Task WebRequestTask()
{
try
{
string ResponseText = await httpClient.GetStringAsync("https://fakeurl.com");
// process response code
}
catch (Exception ex)
{
// handle error
}
}
这是我要创建任务的主要方法:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Task RequestTask = ... // here I want to initialize the task without starting
// chain continuation tasks to RequestTask
RequestTask.Start();
}
我已经尝试了以下解决方案,但没有满足我的需求:
解决方案 1
Task RequestTask = new Task(WebRequestTask);
Task RequestTask = WebRequestTask;
-> 两者都会导致编译器错误
解决方案 2
Task RequestTask = Task.Run(WebRequestTask);
-> 这将启动任务异步并且当前方法继续(但这里可能会在链接继续任务之前抛出异常)
解决方案 3
Task RequestTask = WebRequestTask();
-> 这会同步启动任务,并在任务完成后发生链接
解决方案 4
Task<Task> OuterTask = new Task<Task>(LoginToAzure);
await LoginAzureTask.Unwrap();
-> 这会启动外部任务,但永远不会调用内部任务
如何将此 Task 方法附加到 Task 对象,以便我可以先附加延续 tasks/set 一些选项,然后启动它?如果可能的话,我也想使用取消令牌。
I want to create/initialize a Task object from a async Task method without starting it directly.
最好的方法是使用委托。例如:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Func<Task> taskFactory = () => WebRequestTask(TokenSource);
// chain continuation tasks
Task task = taskFactory(); // Start the task
... // await the task or save it somewhere
}
旁注:我强烈建议使用 await
而不是 ContinueWith
来实现“链延续任务”。 await
是一种干净安全的方法; ContinueWith
是一种低级、危险的方法。
使用 async
关键字实现的异步方法正在创建“热”Task
,换句话说,Task
已经启动。您可以通过将 async
方法包装在另一个 Task
中来延迟其执行,从而创建嵌套的 Task<Task>
,如下所示:
{
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
Task<Task> taskTask = new Task<Task>(() => WebRequestAsync(token));
Task task = taskTask.Unwrap(); // The task is not started yet
// Chain continuations to task
taskTask.RunSynchronously(TaskScheduler.Default);
// The task is now started
}
private async Task WebRequestAsync(CancellationToken cancellationToken)
{
try
{
string responseText = await httpClient.GetStringAsync(
"https://fakeurl.com", cancellationToken);
// Process response code
}
catch (Exception ex)
{
// Handle error
}
}
taskTask
变量是仅表示 WebRequestAsync
方法执行的包装器,而不是此方法创建的 Task
的完成。包装器任务在调用RunSynchronously
method, and after that point your code will have no use for it. The task
variable "unwraps包装器后立即完成,代表实际的异步Web请求。当异步操作完成时完成。
如您所见,此技术相当麻烦。你很少会在应用程序代码中看到它,因为纯异步等待组合使用起来要方便得多。
I want to do exception handling as well, so it can't be an async void
.
因为您正在处理 WebRequestTask() 内部的错误,所以这并不重要。
首选选项是将 StartTask 设置为 async Task
,但如果不可能,请将其设置为 async void
。这也满足了“不启动”的要求。
所以,保持简单:
private async void StartTask()
{
TokenSource = new CancellationTokenSource();
try
{
await WebRequestTask(TokenSource.Token);
// here I want to initialize the task without starting -- irrelevant in an async void
await otherTask(); // continuation(s)
}
catch()
{
// handle residual errors
}
}
我想从异步任务方法 create/initialize 任务对象而不直接启动它。在线搜索时,我只找到从 void 方法创建的任务的答案。
这是我要执行的任务方法。因为我需要做一些异步的网络请求,所以该方法需要是异步的。我也想做异常处理,所以它不能是 async void
.
private async Task WebRequestTask()
{
try
{
string ResponseText = await httpClient.GetStringAsync("https://fakeurl.com");
// process response code
}
catch (Exception ex)
{
// handle error
}
}
这是我要创建任务的主要方法:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Task RequestTask = ... // here I want to initialize the task without starting
// chain continuation tasks to RequestTask
RequestTask.Start();
}
我已经尝试了以下解决方案,但没有满足我的需求:
解决方案 1
Task RequestTask = new Task(WebRequestTask);
Task RequestTask = WebRequestTask;
-> 两者都会导致编译器错误
解决方案 2
Task RequestTask = Task.Run(WebRequestTask);
-> 这将启动任务异步并且当前方法继续(但这里可能会在链接继续任务之前抛出异常)
解决方案 3
Task RequestTask = WebRequestTask();
-> 这会同步启动任务,并在任务完成后发生链接
解决方案 4
Task<Task> OuterTask = new Task<Task>(LoginToAzure);
await LoginAzureTask.Unwrap();
-> 这会启动外部任务,但永远不会调用内部任务
如何将此 Task 方法附加到 Task 对象,以便我可以先附加延续 tasks/set 一些选项,然后启动它?如果可能的话,我也想使用取消令牌。
I want to create/initialize a Task object from a async Task method without starting it directly.
最好的方法是使用委托。例如:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Func<Task> taskFactory = () => WebRequestTask(TokenSource);
// chain continuation tasks
Task task = taskFactory(); // Start the task
... // await the task or save it somewhere
}
旁注:我强烈建议使用 await
而不是 ContinueWith
来实现“链延续任务”。 await
是一种干净安全的方法; ContinueWith
是一种低级、危险的方法。
使用 async
关键字实现的异步方法正在创建“热”Task
,换句话说,Task
已经启动。您可以通过将 async
方法包装在另一个 Task
中来延迟其执行,从而创建嵌套的 Task<Task>
,如下所示:
{
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
Task<Task> taskTask = new Task<Task>(() => WebRequestAsync(token));
Task task = taskTask.Unwrap(); // The task is not started yet
// Chain continuations to task
taskTask.RunSynchronously(TaskScheduler.Default);
// The task is now started
}
private async Task WebRequestAsync(CancellationToken cancellationToken)
{
try
{
string responseText = await httpClient.GetStringAsync(
"https://fakeurl.com", cancellationToken);
// Process response code
}
catch (Exception ex)
{
// Handle error
}
}
taskTask
变量是仅表示 WebRequestAsync
方法执行的包装器,而不是此方法创建的 Task
的完成。包装器任务在调用RunSynchronously
method, and after that point your code will have no use for it. The task
variable "unwraps包装器后立即完成,代表实际的异步Web请求。当异步操作完成时完成。
如您所见,此技术相当麻烦。你很少会在应用程序代码中看到它,因为纯异步等待组合使用起来要方便得多。
I want to do exception handling as well, so it can't be an
async void
.
因为您正在处理 WebRequestTask() 内部的错误,所以这并不重要。
首选选项是将 StartTask 设置为 async Task
,但如果不可能,请将其设置为 async void
。这也满足了“不启动”的要求。
所以,保持简单:
private async void StartTask()
{
TokenSource = new CancellationTokenSource();
try
{
await WebRequestTask(TokenSource.Token);
// here I want to initialize the task without starting -- irrelevant in an async void
await otherTask(); // continuation(s)
}
catch()
{
// handle residual errors
}
}