Important for CPS users: If you're in CPS or a CPS extension, please
replace all references to ThreadHelper.JoinableTaskFactory
with
this.ThreadingService.JoinableTaskFactory
, where this.ThreadingService
is an [Import] IProjectThreadingService
.
- Reference Microsoft.VisualStudio.Threading.dll
- Add
using Microsoft.VisualStudio.Threading;
to the start of any relevant C# source files.
this.ThreadingService.JoinableTaskFactory.Run(async delegate
{
// caller's thread
await SomeOperationAsync(...);
// switch to main thread
await this.ThreadingService.JoinableTaskFactory.SwitchToMainThreadAsync();
SomeUIThreadBoundWork();
// switch to threadpool
await TaskScheduler.Default; // using Microsoft.VisualStudio.Threading;
SomeWorkThatCanBeOnThreadpool();
});
If any of your async work should be on a background thread, you can switch explicitly:
// assuming we're on the UI thread already…
await Task.Run(async delegate
{
// we're on a background thread now
});
// ...now we're back on the UI thread.
Alternatively:
// On some thread, but definitely want to be on the threadpool:
// using Microsoft.VisualStudio.Threading;
await TaskScheduler.Default;
// On some thread, but definitely want to be on the main thread:
await this.ThreadingService.JoinableTaskFactory.SwitchToMainThreadAsync();
await this.ThreadingService.JoinableTaskFactory.SwitchToMainThreadAsync();
First, consider carefully whether you can make your method async first and then follow the above pattern. If you must remain synchronous, you can switch to the UI thread like this:
this.ThreadingService.JoinableTaskFactory.Run(async delegate
{
await this.ThreadingService.JoinableTaskFactory.SwitchToMainThreadAsync();
// You're now on the UI thread.
});
Simply call the JoinableTaskFactory.RunAsync
extension method (defined in
the Microsoft.VisualStudio.Shell
namespace) passing in a priority as the
first parameter, like this:
await ThreadHelper.JoinableTaskFactory.RunAsync(
VsTaskRunContext.UIThreadBackgroundPriority,
async delegate
{
// On caller's thread. Switch to main thread (if we're not already there).
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
// Now on UI thread via background priority.
await Task.Yield();
// Resumed on UI thread, also via background priority.
});
How can I complete asynchronously with respect to my caller while still using the UI thread?
If you need to complete work on the UI thread, and your caller is
(usually) on the UI thread, but you want to return a Task<TResult>
to
your caller and complete the work asynchronously, you should use the
JoinableTaskFactory.RunAsync
method, as described in the answer to: How
do I switch to the UI thread using a specific priority?
Make sure you have these using directives at the top of your code file:
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell;
using Task = System.Threading.Tasks.Task;
That adds a GetAwaiter
extension
method
to IVsTask
that makes it awaitable. In fact just the Microsoft.VisualStudio.Shell
namespace is required for that, but the first one is very useful. And since
they both define Task classes they conflict making TPL Task difficult to
use unless you add the third line.
The preferred way is to use the JoinableTaskFactory.RunAsyncAsVsTask(Func<CancellationToken, Task>)
extension method. This gives you a CancellationToken
that is tied
to IVsTask.Cancel()
.
public IVsTask DoAsync()
{
return ThreadHelper.JoinableTaskFactory.RunAsyncAsVsTask(
VsTaskRunContext.UIThreadNormalPriority, // (or lower UI thread priorities)
async cancellationToken =>
{
await SomethingAsync(cancellationToken);
await SomethingElseAsync();
});
}
private async Task SomethingAsync(CancellationToken cancellationToken)
{
await Task.Yield();
}
Alternately, if you only have a JoinableTask
, you can readily convert it
to an IVsTask
by calling the JoinableTask.AsVsTask()
extension method.
public IVsTask DoAsync()
{
return ThreadHelper.JoinableTaskFactory.RunAsync(
async delegate
{
await SomethingAsync();
await SomethingElseAsync();
}).AsVsTask();
}