我在 Windows 服务中有一个线程 (STAThread),它执行大量工作。当 Windows 服务重新启动时,我想优雅地停止这个线程。
我知道几种方法
据我发现 Thread.Abort 是不行的......
最佳做法是什么?这项工作是在另一个类中执行的,而不是线程启动的类,因此有必要在构造函数中引入一个 cancelToken 参数,或者例如有一个 volatile 变量。但我就是不知道什么是最聪明的。
更新
只是为了澄清一点,我已经总结了我正在谈论的一个非常简单的例子。如前所述,这是在 Windows 服务中完成的。现在我正在考虑一个在循环中检查的可变布尔值或一个取消令牌......我不能等待循环完成,如下所述,它可能需要几分钟,使服务器的系统管理员相信当他们需要重新启动服务时,服务出了点问题......我可以毫无问题地将循环中的所有工作都毫无问题地删除,但是我不能用 Thread.Abort 来做到这一点,它是“邪恶的”,而且是一个 COM接口被调用,所以需要一个小的清理。
Class Scheduler{
private Thread apartmentThread;
private Worker worker;
void Scheduling(){
worker = new Worker();
apartmentThread = new Thread(Run);
apartmentThread.SetApartmentState(ApartmentState.STA);
apartmentThread.Start();
}
private void Run() {
while (!token.IsCancellationRequested) {
Thread.Sleep(pollInterval * MillisecondsToSeconds);
if (!token.IsCancellationRequested) {
worker.DoWork();
}
}
}
}
Class Worker{
//This will take several minutes....
public void DoWork(){
for(int i = 0; i < …Run Code Online (Sandbox Code Playgroud) 将令牌放在任务的构造函数中的原因很多,如下所述: 任务构造器中的取消令牌:为什么?
通过使用关键字async / await,它如何工作?例如下面的代码:
public async Task MethodAsync(CancellationToken token)
{
await Method01Async();
await Method02Async();
}
Run Code Online (Sandbox Code Playgroud)
虽然这是一个异步过程。我很快就使用了“ Task.StartNext”或“ Task.Run”或“新任务”。为了能够指定我的取消令牌,该怎么办?
我有一个总是被阻止的任务,我有一个 CancellationToken 传递给它,用于取消任务。然而,设置为在任务取消时执行的继续任务永远不会执行。代码是:
_tokenSrc = new CancellationTokenSource();
var cnlToken = _tokenSrc.Token;
Task.Run(() =>
// _stream.StartStream() blocks forever
_stream.StartStream(), cnlToken)
.ContinueWith(ant =>
{
_logger.Warn("Stream task cancellation requested, stopping the stream");
_stream.StopStream();
_stream = null;
_logger.Warn("Stream stopped and task cancelled");
}, TaskContinuationOptions.OnlyOnCanceled);
Run Code Online (Sandbox Code Playgroud)
稍后在代码中的其他地方......
_tokenSrc.Cancel();
Run Code Online (Sandbox Code Playgroud)
我不得不为 _stream.StartStream() 使用 Task 的原因是这个调用永远阻塞(一个我无法控制的 api,请注意 _stream 指的是一个第三方 Api,它从 web 服务流数据)所以我不得不调用它在另一个线程上。
取消任务的最佳方法是什么?
在这种情况下预期的是,如果用户通过按Enter键取消任务,则挂起的另一个任务ContinueWith将运行,但事实并非如此,AggregateException尽管显式处理ContinueWith显然没有被执行,但仍保持抛出.
请问下面的任何澄清?
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokensource = new CancellationTokenSource();
CancellationToken token = tokensource.Token;
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
t.Exception.Handle((e) => true);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
Console.WriteLine("Press any key to cancel");
Console.ReadLine();
tokensource.Cancel();
task.Wait();
}
}
Run Code Online (Sandbox Code Playgroud) .net c# multithreading task-parallel-library cancellation-token
是否可以在不知道当前正在运行的情况下取消所有异步方法?
例如,我有几个可能运行异步任务的类:
class Class1
{
public async void SomeTask()
{
for (int i = 0; i < 5; i++)
{
// Doing some job
await Task.Delay(2000);
}
}
}
class Class2
{
public async void ContinuouslyTask()
{
for (;;)
{
// Doing some job on background
await Task.Delay(1000);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想在注销之前关闭每个异步任务:
class Program
{
static void Main(string[] args)
{
var p = new Program();
var c1 = new Class1();
var c2 = new Class2();
c1.SomeTask();
c2.ContinuouslyTask();
while (Console.ReadKey().Key != …Run Code Online (Sandbox Code Playgroud) 我正在研究通过 WebAPI 取消异步请求,
最近我发现最新版本的 WebApi(不是 .NET-Core 的)支持取消令牌。作为测试,我编写了这段代码。
[HttpGet]
[Route("LongRequest")]
public async Task<string> VeryLongRequest(string Key, CancellationToken token)
{
for (int i = 0; i < 5; ++i)
{
if (false == token.IsCancellationRequested)
{
Thread.Sleep(5000);
}
else
{
Trace.WriteLine("Cancelled");
}
}
return "Complete";
}
Run Code Online (Sandbox Code Playgroud)
当用户关闭窗口/刷新/或导航到新页面(不在 SPA 上)时,此代码有效,并且取消令牌会更新。
由于 HTTP 请求假设是无状态的,取消映射如何映射回请求?
以及如何从我的客户手动调用取消?
c# asynchronous stateless asp.net-web-api cancellation-token
我最近正在学习和使用 Polly 来为我的代码添加弹性,特别是对于超时和重试策略。但是,我不知道如何使用 polly 对代码进行单元测试。更具体地说,我不知道如何模拟以 Cancellation Token 作为参数的方法。下面是我的代码结构
public class Caller
{
private IHttpManager httpManager;
private IAsyncPolicy<HttpResponseMessage> policyWrap;
public Caller(IHttpManager httpManager, IAsyncPolicy<HttpResponseMessage> policyWrap)
{
this.httpManager= httpManager;
this.policyWrap = policyWrap;
}
public async Task CallThirdParty()
{
HttpResponseMessage httpResponse = await policyWrap.ExecuteAsync(async ct => await httpManager.TryCallThirdParty(ct), CancellationToken.None);
}
}
public interface IHttpManager
{
Task<HttpResponseMessage> TryCallThirdParty(CancellationToken cancellationToken);
}
Run Code Online (Sandbox Code Playgroud)
下面是我打算运行但不知道如何运行的单元测试。
[Test]
public void TestBehaviourUnderTimeoutPolicy()
{
// Set up the timeout policy such that the governed delegate will terminate after 1 sec if no response …Run Code Online (Sandbox Code Playgroud) 我正在使用 Asp.net Core API 并设置如下服务:
services
.Configure<AppOptions>(_configuration.GetSection("app"))
.AddMvcCore(options =>
{
options.RespectBrowserAcceptHeader = true;
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
options.InputFormatters.Add(new XmlSerializerInputFormatter(options));
})
.AddFormatterMappings()
.AddJsonFormatters()
.AddXmlSerializerFormatters()
.AddCors();
Run Code Online (Sandbox Code Playgroud)
之后,我创建了一个带有 CancellationToken 参数的 API,如下所示:
[HttpGet("list")]
public async Task<ActionResult<IEnumerable<string>>> Get(CancellationToken cancellationToken)
{
var result = await _orderBusinessService.GetList(cancellationToken);
return Ok(result);
}
Run Code Online (Sandbox Code Playgroud)
当我从 Postman 或浏览器调用此 API 时,我得到以下响应:
415 不支持的媒体类型
当我添加[FromQuery]到cancellationToken时,就可以了。
实际上,这似乎CancellationTokenModelBinder行不通。
我不知道为什么?有人有任何想法吗?
我有交互式任务,在“最坏”场景中根本不执行,因此它由TaskCompletionSource.
我想等待此任务完成,或者我收到的令牌被取消——以先发生者为准。这种工作的完美工具将是Task.WhenAny,唯一的问题是它只需要任务,而我有一个Task和一个CancellationToken。
如何等待(异步地,像Task.WhenAny)触发的第一个事件——完成的任务或取消的令牌?
async Task MyCodeAsync(CancellationToken token)
{
var tcs = new TaskCompletionSource<UserData>(); // represents interactive part
await Task.WhenAny(tcs.Task, token); // imaginary call
UserData data = tcs.Task.Result; // user interacted, let's continue
...
}
Run Code Online (Sandbox Code Playgroud)
我不创建/管理令牌,所以我无法更改它。我必须处理它。
更新:对于这种特殊情况,可以使用Register令牌上的方法取消TaskCompletionSource. 有关更通用的方法,请参阅 Matthew Watson 的回答。
我正在使用一个支持取消的异步 api,我正在向该 api 传递一个CancellationToken实例。像往常一样,如果在传递的令牌上请求取消,我正在调用的 api 将抛出一个OperationCanceledException(这是 .NET 框架的标准合作取消模式)。
我希望能够捕获OperationCanceledException 当且仅当它是由于取消提供的取消令牌而引发的异常时。
以下代码说明了我要实现的目标:
try
{
await _service.DoSomethingAsync(cancellationToken: token);
}
catch (OperationCanceledException ex) when ( /* here I want a condition signifying that the OperationCanceledException is caused by the cancellation of the token object */)
{
// avoid logging the exception: this is raised by design (cooperative cancellation)
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred: {0}", ex.Message);
throw;
}
Run Code Online (Sandbox Code Playgroud)
对于上面代码的异常过滤器,我基本上有两个想法:
IsCancellationRequested属性token …c# ×10
async-await ×4
asynchronous ×3
cancellation ×2
.net ×1
.net-core ×1
asp.net-core ×1
async-ctp ×1
moq ×1
polly ×1
stateless ×1
task ×1