Задача продолжения в той же теме, что и предыдущая
У меня есть WebService, который создает задачу и задачу продолжения.
В первой задаче мы устанавливаем Thread.CurrentPrincipal
Следовательно, когда запускается ContinuationTask, у него больше нет Thread.CurrentPrincipal.
Я хотел бы указать в ContinuationTask, что он должен работать в том же потоке, что и его предшественник.
Я искал в Интернете, но я нашел требование для потока для запуска в SynchronizationContext, поэтому я начинаю думать, что мне не хватает какого-то основного правила, особенно в отношении того, как Thread.Principal должен работать.
Ответы
Ответ 1
Прежде всего, не используйте TaskContinuationOptions.ExecuteSynchronously
для этой цели! Вы не можете принудительно продолжить продолжение в одном потоке. Он работает только с очень высокой вероятностью. Всегда есть случаи, когда это не работает: слишком большая рекурсия приведет к тому, что TPL не будет выполняться синхронно. Пользовательские TaskScheduler
также не обязаны поддерживать это.
Это распространенное заблуждение, особенно потому, что оно неправильно распространено в Интернете. Вот некоторые чтения по этой теме: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx
Если вам нужно запустить один поток, сделайте следующее:
Task.Factory.StartNew(() => { First(); Second(); });
Так просто.
Позвольте мне проиллюстрировать, почему это работает, показывая альтернативное решение:
void MyCompositeTask()
{
var result = First();
Second(result);
}
Task.Factory.StartNew(() => MyCompositeTask());
Это выглядит более интуитивно: мы передаем MyCompositeTask
в TPL для запуска. TPL не заботится о том, что мы делаем в нашем обратном вызове. Мы можем делать все, что захотим, включая вызов нескольких методов и передачу результатов.
Ответ 2
Из моего учебника по С# (С# 4.0 в двух словах):
Вы можете заставить их [задачи продолжения] выполнить в том же потоке [как их антецедент], указав TaskContinuationOptions.ExecuteSynchronously
при вызове ContinueWith
: это может улучшить производительность в очень мелкозернистых продолжениях с уменьшением косвенности.
В принципе, я не пробовал это, но, похоже, это то, что вы ищете, и может использоваться в сочетании с Thread.CurrentPrincipal.
Вот ссылка на статью MSDN и еще несколько конкретных примеров.
Ответ 3
Настройка идентификатора потока пула не является хорошей идеей. Он связывает вас с этим конкретным потоком и рискует "утечить" личность в случае исключений, если вы забудете очистить личность в обработчике исключений. Вы можете столкнуться с несвязанными задачами, запущенными с использованием "утечки".
Попробуйте передать объект WindowsIdentity в задачи и выдать себя за использование WindowsIdentity.Impersonate. Это позволит вам использовать любой доступный поток и безопасно очистить личность, даже если возникает исключение.
Вы можете попробовать что-то вроде этого:
WindowsPrincipal myPrincipal=...;
...
var identity=(WindowsIdentity)myPrincipal.Identity;
var task=Task.Factory.StartNew(ident=>{
var id=(WindowsIdentity)ident;
using(var context=id.Impersonate())
{
//Work using the impersonated identity here
}
return id;
},identity).
.ContinueWith(r=>{
var id = r.Result;
using(var context=id.Impersonate())
{
//Work using the impersonated identity here
}
});
Операторы using
гарантируют, что выданный личность очищается, даже если возникает исключение.
Ответ 4
Вызвать продолжение с помощью TaskScheduler.FromCurrentSynchronizationContext():
Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
Скопировано из fooobar.com/info/36359/...