Один простой вопрос, прежде чем читать длинный ответ ниже:
Зачем вам нужна такая же тема? Получаете ли вы доступ к потоку stati c / contextual data?
Если да, то есть способы решить эту проблему проще, чем ограничить выполнение задач в одном потоке.
Как ограничить выполнение задач в одном потоке
Пока вы используете вызовы asyn c в контексте синхронизации по умолчанию и как только код возобновляется из await
, возможно, что поток может измениться после await
. Это связано с тем, что контекст по умолчанию планирует задачи для следующего доступного потока в пуле потоков. Как и в приведенном ниже случае, before
может отличаться от after
:
public async Task ScrapeObjects(int page = 1)
{
var before = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(1000);
var after = Thread.CurrentThread.ManagedThreadId;
}
Единственный надежный способ гарантировать, что ваш код может вернуться в тот же поток, - это запланировать асинхронное выполнение c код для однопоточного контекста синхронизации:
class SingleThreadSynchronizationContext : SynchronizationContext
{
private readonly BlockingCollection<Action> _actions = new BlockingCollection<Action>();
private readonly Thread _theThread;
public SingleThreadSynchronizationContext()
{
_theThread = new Thread(DoWork);
_theThread.IsBackground = true;
_theThread.Start();
}
public override void Send(SendOrPostCallback d, object state)
{
// Send requires run the delegate immediately.
d(state);
}
public override void Post(SendOrPostCallback d, object state)
{
// Schedule the action by adding to blocking collection.
_actions.Add(() => d(state));
}
private void DoWork()
{
// Keep picking up actions to run from the collection.
while (!_actions.IsAddingCompleted)
{
try
{
var action = _actions.Take();
action();
}
catch (InvalidOperationException)
{
break;
}
}
}
}
И вам нужно запланировать ScrapeObjects
в пользовательский контекст:
SynchronizationContext.SetSynchronizationContext(new SingleThreadSynchronizationContext());
await Task.Factory.StartNew(
() => ScrapeObjects(),
CancellationToken.None,
TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
TaskScheduler.FromCurrentSynchronizationContext()
).Unwrap();
Таким образом, весь ваш асин c код должен быть запланирован на тот же контекст и выполняться потоком в этом контексте.
Однако
Это обычно опасно , как вы вдруг потерять способность использовать пул потоков. Если вы заблокируете поток, вся асинхронная операция c будет заблокирована, то есть у вас будут взаимоблокировки.