gpt4 book ai didi

c# - 任务继续被安排到非线程池线程。为什么?

转载 作者:太空宇宙 更新时间:2023-11-03 15:57:27 24 4
gpt4 key购买 nike

在我的控制台应用程序中,我确实创建了自己的线程来实现工作队列。此外,我已经为这个唯一的线程实现了我自己的 SynchronizationContext。

当我等待来自主线程的任务时,突然将继续(例程的剩余部分)安排到我的工作线程上,这是错误的,因为我不希望我的线程将用作随机任务的线程池线程。

我仅在使用 Mono 运行代码时遇到此行为。

这是在单声道上重现问题的代码(在 mac os x 和 linux 系统上测试):

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static void Main( string[] args )
{
Foo();
Console.ReadLine();
}


async static void Foo()
{
Console.WriteLine( "{0}: current thread ID={1}; scheduler={2}; context={3};",
" Main BEFORE awaiting",
Thread.CurrentThread.ManagedThreadId,
TaskScheduler.Current.Id,
SynchronizationContext.Current != null );
// MONO Output: Main BEFORE awaiting: current thread ID=1; scheduler=1; context=False;

WorkQueue queue = new WorkQueue();

// !!!
// I do expect that current context which is null will be captured for continuation.
// !!!
await queue.Enqueue();

// !!!
// As we can see our custom context was captured to continue with this part of code.
//
Console.WriteLine( "{0}: current thread ID={1}; scheduler={2}; context={3};",
" Main AFTER awaiting",
Thread.CurrentThread.ManagedThreadId,
TaskScheduler.Current.Id,
SynchronizationContext.Current != null );
// MONO Output: Main AFTER awaiting: current thread ID=4; scheduler=1; context=True;
}
}

// Custom context which does nothing but enqueues fake tasks to the queue.
//
class WorkQueueSyncContext : SynchronizationContext
{
readonly WorkQueue queue;

public WorkQueueSyncContext( WorkQueue queue )
{
this.queue = queue;
}

public override void Post( SendOrPostCallback d, object state )
{
}

public override void Send( SendOrPostCallback d, object state )
{
queue.Enqueue().Wait();
}
}

// The queue
//
class WorkQueue
{
readonly Thread thread;

class WorkQueueItem
{
public TaskCompletionSource<object> Completion
{
get;
set;
}
}

BlockingCollection<WorkQueueItem> queue = new BlockingCollection<WorkQueueItem>();


public WorkQueue()
{
thread = new Thread( new ThreadStart( Run ) );
thread.Start();
}

private void Run()
{
// Set ower own SynchronizationContext.
//
SynchronizationContext.SetSynchronizationContext( new WorkQueueSyncContext( this ) );

Console.WriteLine( "{0}: current thread ID={1}; scheduler={2}; context={3};",
" WorkQueue START",
Thread.CurrentThread.ManagedThreadId,
TaskScheduler.Current.Id,
SynchronizationContext.Current != null );
// MONO Output: current thread ID=4; scheduler=1; context=True;

// Working loop.
//
while ( true )
{
WorkQueueItem item = queue.Take();

Console.WriteLine( "{0}: current thread ID={1}; scheduler={2}; context={3};",
" WorkQueue DOING TASK",
Thread.CurrentThread.ManagedThreadId,
TaskScheduler.Current.Id,
SynchronizationContext.Current != null );
// MONO Output: current thread ID=4; scheduler=1; context=True;

// Completed the task :)
//
item.Completion.SetResult( true );
}
}

public Task<object> Enqueue()
{
TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
queue.Add( new WorkQueueItem() { Completion = completion } );
return completion.Task;
}
}

所以,这里是 MONO 输出:

   Main BEFORE awaiting: current thread ID=1; scheduler=1; context=False;
WorkQueue START: current thread ID=3; scheduler=1; context=True;
WorkQueue DOING TASK: current thread ID=3; scheduler=1; context=True;
Main AFTER awaiting: current thread ID=3; scheduler=1; context=True;

这是 Windows 输出:

   Main BEFORE awaiting: current thread ID=10; scheduler=1; context=False;
WorkQueue START: current thread ID=11; scheduler=1; context=True;
WorkQueue DOING TASK: current thread ID=11; scheduler=1; context=True;
Main AFTER awaiting: current thread ID=6; scheduler=1; context=False;

请注意(最后一行)上下文捕获有何不同。

编辑:

无法用 Mono 3.4.0 重现,因此似乎是旧版本(至少 3.2.6)中的错误;

最佳答案

我认为您在 Mono 运行时中发现了一个错误。 await 之后的继续不应发生在具有与 TaskAwaiter 捕获的线程不同的同步上下文的线程上,在 await 点。

可能出现以下情况:

  1. 原始线程和完成线程都有相同的同步上下文。延续可以内联(在完成线程上同步执行)。
  2. 原始线程和完成线程都没有同步上下文(SynchronizationContext.Current == null)。 continuation 仍然可以内联。
  3. 在任何其他组合中,continuation 不得内联。

我所说的“可能内联”是指不需要或保证如此(它仍然可以使用 TaskScheduler.CurrentTaskScheduler.FromCurrentSynchronizationContext 进行异步安排执行)。尽管如此,在当前 Microsoft 的 TPL 实现下,它确实会针对条件 #1 和 #2 进行内联。

但是,#3 不能内联,这是常识决定的。因此,请随时向 Xamarin 报告错误。首先尝试最新的 Mono 版本,看看问题是否仍然存在。

关于c# - 任务继续被安排到非线程池线程。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22965202/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com