How to create a task (TPL) running a STA thread?
You can use the TaskScheduler.FromCurrentSynchronizationContext Method to get a TaskScheduler for the current synchronization context (which is the WPF dispatcher when you're running a WPF application).
Then use the ContinueWith overload that accepts a TaskScheduler:
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(...)
.ContinueWith(r => AddControlsToGrid(r.Result), scheduler);
How to fix Task.Run from UI thread throwing STA error
The COM components accessed through Interop require the calling thread to be a STA thread but in your case it is not STA. Otherwise the STA component could be accessed through multiple threads. You can read more about why STA is required in Understanding and Using COM Threading Models.
You can make a extension method on Task class as suggested in Set ApartmentState on a Task to call the COM component through Interop using task:
public static Task<T> StartSTATask<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
Thread thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
}
When you use Thread instead of task, you have to set the ApartmentState to STA using something like thread.SetApartmentState(ApartmentState.STA)
.
Async/await, ThreadPool and SetApartmentState
Stephen Toub has already written an StaTaskScheduler
(archive); I recommend you use that.
You can then construct a TaskFactory
using that TaskScheduler
. There is no equivalent to Task.Run
on the TaskFactory
type but you can easily create one using StartNew
and Unwrap
.
How to force a task to run on an STA thread?
I have WinForms application, and I want to use the Windows.Forms.OpenFileDialog, which requires an STA thread.
Yes. Every WinForms app has an STA thread which is its main thread. It's easiest to just use that thread.
It works fine with threads, but what about tasks? ... How to create a task (TPL) running a STA thread?
Apartment threading models is a thread concept. There's no such thing as an STA task.
However, a Delegate Task can run on an STA thread if it is scheduled to that thread.
I don't understand how TaskScheduler.FromCurrentSynchronizationContext() is supposed to force a task to run on a STA thread
It doesn't always do that. TaskScheduler.FromCurrentSynchronizationContext
will create a TaskScheduler
that schedules tasks to SynchronizationContext.Current
. Now, on WinForms, if the calling code is on the UI thread, then SynchronizationContext.Current
is a WindowsFormsSynchronizationContext
instance. That WindowsFormsSynchronizationContext
will execute code on the UI thread. So, the TaskScheduler
will schedule tasks to the UI thread (which is an STA thread), and the tasks end up running on the existing STA thread. Again, that is if TaskScheduler.FromCurrentSynchronizationContext
was called from the UI thread in the first place.
If TaskScheduler.FromCurrentSynchronizationContext
is called from a thread pool thread, then SynchronizationContext.Current
is null
, and the resulting TaskScheduler
schedules tasks on a thread pool thread, which is not STA.
So how do I forcibly start a task on STA apartment state, so that I can use components that require it in my application?
The optimum way to do this is to structure your code so that the UI thread calls the background threads, not the other way around. Don't have your background threads call the UI to do things. If the UI is in control, then simpler patterns like await
and IProgress<T>
can be used to coordinate with the background threads. If background thread(s) must drive the UI, then one solution is to capture the UI SynchronizationContext
(or TaskScheduler
) and use that from a background thread to execute code on the UI thread.
WatiN: The CurrentThread needs to have it's ApartmentState set to ApartmentState.STA to be able to automate Internet Explorer
I know Benjamin's already posted a 'working' answer, but I thought I'd add a couple of things I've experienced when I've got this error when trying to execute WatiN tests:
For NUnit, you should add something like this to your app.config for the tests:
<configSections>
<sectionGroup name="NUnit">
<section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/>
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
<!-- WatiN can only host IE in STA mode -->
<add key="ApartmentState" value="STA"/>
</TestRunner>
</NUnit>
In MbUnit, modify your TestFixture attribute like this:
[TestFixture(ApartmentState = ApartmentState.STA)]
HTH,
Pete.
Ha - it's actually in the documentation. Doh!
http://watin.org/documentation/sta-apartmentstate/
Is it possible to await Thread in C#
Here is an extension method you could use to enable the awaiting of threads (inspired from this article: await anything).
public static TaskAwaiter GetAwaiter(this Thread thread)
{
return Task.Run(async () =>
{
while (thread.IsAlive)
{
await Task.Delay(100).ConfigureAwait(false);
}
}).GetAwaiter();
}
Usage example:
var thread = new Thread(() =>
{
Thread.Sleep(1000); // Simulate some background work
});
thread.IsBackground = true;
thread.Start();
await thread; // Wait asynchronously until the thread is completed
thread.Join(); // If you want to be extra sure that the thread has finished
Run code and return value in a Single-Threaded Apartment, where I can't set current apartment model
There is a slightly better way. You will have to create a new thread to guarantee that you are on an STA thread since you can't change a thread's apartment state after it starts. However you can get rid of the Thread.Join()
call so that your method is actual asynchronous using TaskCompletionSource:
private static async Task<string> GetToken(string authority, string resource, string scope) // I don't control this signature, as it gets passed as a delegate
{
using (var tcs = new TaskCompletionSource<string>()) {
Thread t = new Thread(() => GetAuthToken(tcs));
t.SetApartmentState(ApartmentState.STA);
t.Start();
var token = await tcs.Task
return token;
}
}
private static void GetAuthToken(TaskCompletionSource<string> tcs)
{
try {
Credentials creds = AuthManagement.CreateCredentials(args); // this call must be STA
tcs.SetResult(creds.Token);
}
catch(Exception ex) {
tcs.SetException(ex);
}
}
Also if you need to wrap a return value in a task, use Task.FromResult()
instead of Task.Run()
.
Related Topics
How to Convert an Object to a Byte Array in C#
How to Get a JSON String from Url
How to Return Text from Native (C++) Code
How to Add CSS Class to HTML Generic Control Div
Firefox Browser Does Not Reload the Update CSS/Js Files
Arkit Body Tracking Using Xamarin and C# Inaccurate
How Would You Code an Efficient Circular Buffer in Java or C#
How to Upload a File to an Sftp Server in C# (.Net)
How to Indefinitely Pause a Thread
Ef Code-First One-To-One Relationship: Multiplicity Is Not Valid in Role * in Relationship
C# Covariant Return Types Utilizing Generics
Why Is the Iteration Variable in a C# Foreach Statement Read-Only
How to Find Multiple Occurrences with Regex Groups
Remote Validation for List of Models
In C#, What Happens When You Call an Extension Method on a Null Object