Introduction
This is a lengthy question! You will find some background on the problem at the beginning, then code samples, which have been simplified for representation and the Question after that. Please read in any order that you find good for you!
Background information
I am writing a Proof-of-Concept part for an application for communicating with an STA COM. This part of the application has the requirement of running in a Single-Threaded Apartment (STA) context in order to communicate with said STA COM. The rest of the application runs in a MTA context.
Current state
What I have come up with so far is creating a Communication class that contains a while loop, running in a STA. The work that needs to be relayed to the COM object is queued from the outside to the Communication class via ConcurrentQueue. The work items are then dequeued in the while loop and the work is performed.
Code context
Communication class
This is a static class, containing a loop that is intended to run in STA state and check if some work needs to be done by the COM and dispatch the work to the handler.
static class Communication
{
#region Public Events
/// This event is raised when the COM object has been initialized
public static event EventHandler OnCOMInitialized;
#endregion Public Events
#region Private Members
/// Stores a reference to the COM object
private static COMType s_comObject;
/// Used to queue work that needs to be done by the COM object
private static ConcurrentQueue<WorkUnit> s_workQueue;
#endregion Private Members
#region Private Methods
/// Initializes the COM object
private static void InternalInitializeCOM()
{
s_comObject = new COMType();
if (s_comObject.Init())
{
OnCOMInitialized?.Invoke(null, EventArgs.Empty);
}
}
/// Dispatches the work unit to the correct handler
private static void HandleWork(WorkUnit work)
{
switch (work.Command)
{
case WorkCommand.Initialize:
InternalInitializeCOM();
break;
default:
break;
}
}
#endregion Private Methods
#region Public Methods
/// Starts the processing loop
public static void StartCommunication()
{
s_workQueue = new ConcurrentQueue<WorkUnit>();
while (true)
{
if (s_workQueue.TryDequeue(out var workUnit))
{
HandleWork(workUnit);
}
// [Place for a delaying logic]
}
}
/// Wraps the work unit creation for the task of Initializing the COM
public static void InitializeCOM()
{
var workUnit = new WorkUnit(
command: WorkCommand.Initialize,
arguments: null
);
s_workQueue.Enqueue(workUnit);
}
#endregion Public Methods
}
Work command
This class describes the work that needs to be done and any arguments that might be provided.
enum WorkCommand
{
Initialize
}
Work unit
This enumeration defines the various tasks that can be performed by the COM.
class WorkUnit
{
#region Public Properties
public WorkCommand Command { get; private set; }
public object[] Arguments { get; private set; }
#endregion Public Properties
#region Constructor
public WorkUnit(WorkCommand command, object[] arguments)
{
Command = command;
Arguments = arguments == null
? new object[0]
: arguments;
}
#endregion Constructor
}
Owner
This is a sample of the class that owns or spawns the Communication with the COM and is an abstraction over the Communication for use in the rest of the application.
class COMController
{
#region Public Events
/// This event is raised when the COM object has been initialized
public event EventHandler OnInitialize;
#endregion Public Events
#region Constructor
/// Creates a new COMController instance and starts the communication
public COMController()
{
var communicationThread = new Thread(() =>
{
Communication.StartCommunication();
});
communicationThread.SetApartmentState(ApartmentState.STA);
communicationThread.Start();
Communication.OnCOMInitialized += HandleCOMInitialized;
}
#endregion Constructor
#region Private Methods
/// Handles the initialized event raised from the Communication
private void HandleCOMInitialized()
{
OnInitialize?.Invoke(this, EventArgs.Emtpy);
}
#endregion Private Methods
#region Public Methods
/// Requests that the COM object be initialized
public void Initialize()
{
Communication.InitializeCOM();
}
#endregion Public Methods
}
The problem
Now, take a look at the Communication.StartCommunication() method, more specifically this part:
...
// [Place for a delaying logic]
...
If this line is substituted with the following:
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);
// OR
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(true);
during inspection the final stop - Communication.InternalInitializeCOM() the apartment of the thread seems to be MTA.
However, if the delaying logic is changed to
Thread.Sleep(100);
the CommunicationInternalInitializeCOM() method seems to be executed in a STA state.
The inspection was done by Thread.CurrentThread.GetApartmentState().
The Question
Can anyone explain to me why does Task.Delay break the STA state? Or am I doing something else that is wrong here?
Thank you!
Thank you for taking all this time to read the question! Have a great day!
Hans has nailed it. Technically, your code is breaking because there's no SynchronizationContext captured by the await. But even if you write one, it won't be enough.
The one big problem with this approach is that your STA thread isn't pumping. STA threads must pump a Win32 message queue, or else they're not STA threads. SetApartmentState(ApartmentState.STA) is just telling the runtime that this is an STA thread; it doesn't make it an STA thread. You have to pump messages for it to be an STA thread.
You can write that message pump yourself, though I don't know of anyone brave enough to have done this. Most people install a message pump from WinForms (a la Hans' answer) or WPF. It may also be possible to do this with a UWP message pump.
One nice side effect of using the provided message pumps is that they also provide a SynchronizationContext (e.g., WinFormsSynchronizationContext / DispatcherSynchronizationContext), so await works naturally. Also, since every .NET UI framework defines a "run this delegate" Win32 message, the underlying Win32 message queue can also contain all the work you want to queue to your thread, so the explicit queue and its "runner" code is no longer necessary.
Because after await Task.Delay() statement , your code runs inside one of the ThreadPool thread, and since the ThreadPool threads are MTA by design.
var th = new Thread(async () =>
{
var beforAwait = Thread.CurrentThread.GetApartmentState(); // ==> STA
await Task.Delay(1000);
var afterAwait = Thread.CurrentThread.GetApartmentState(); // ==> MTA
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
Related
Relatively new to C# and coding in general (first post here). I have a WinForms local application where some information is displayed to the user in a ReadOnly(true) RichTextBox. Almost all my classes need to send information to that RichTextBox. To simplify this process, I created a method inside a static class that uses a locked delegate to send the information to that RichTextBox. Here is a sample:
static class MyClass
{
public delegate void MessageReceivedEventHandler(string message);
public static event MessageReceivedEventHandler messageReceivedEventHandler;
public static void MessageBox(string message)
{
lock (messageReceivedEventHandler)
{
//Thread.Sleep(20);
messageReceivedEventHandler?.Invoke(message);
}
}
}
partial class MyForm : Form
{
public MyForm()
{
MyClass.messageReceivedEventHandler += OnMessageReceived;
}
private void OnMessageReceived(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
MyClass.MessageBox("This should be working!");
//Add more work here...
}
}
The code above would simply print "This should be working!" inside the RichtTextbox.
The problem is the text from richTextBox1 sometimes becoming empty. This issue seems to appear when the MessageBox method is being called in rapid succession. My assumption was that since I have diffent Tasks running at the same time (in other parts of my code), it probably is two Tasks attempting to use the same static ressource, hence the use of Lock. But I still have the issue.
Adding the Thread.Sleep(20) seems to fix the problem, but that is far from elegant/robust. It starts breaking up again when the time inside Sleep is <10ms.
Edit 1:
To clarify what I mean by "string becoming empty", it means the text from richTextBox1 is == "" at some points, which should not happen since the code is always inserting the text, not replacing it. The OnMessageReceived method is the only place where action is taken on the RichTextBox text.
Edit 2:
I saw many questions related to the other tasks running. First, yes it is a multi-threaded application. The only relation between those tasks and my main form is the "print" function I wrote above. To give more context, this application is used to control the position of stepper motors relative to an electrical signal. When doing so, I need to print important information in my main form. This is why losing the information in my RichTextBox (where I print the information) is an issue. The possible reason of why I am losing the text inside that RichTextBox should be the focus of this thread.
Keep in mind that this is a personnal side project, and not a large scale application.
Thanks,
Laurent
There are multiple problems in your code.
First, you should not lock on a public object, since that allows other threads to lock on the same object, risking interlocking your threads. Second, your symptoms suggest multiple threads are trying to access the ressources. Rather than depending on complex thread locking code, you'd rather schedule UI operations on the UI context, which will allow calling adding message from background tasks.
The best way to do that is to that is by using Control.BeginInvoke()
You can't copy your form instance everywhere, so we'll expose a static method. You could make the class a singleton, but if you need multiple instances that won't work. I'll give a more versatile example. When the static method is called, you don't have access to the form instance anymore, so we'll use IOC pattern with an event and delegate.
Let's make a private static event that all instances will register a callback to in the constructor. When the static method raises the static event, all instances callback will be called. The callback will schedule a modification of its text box.
partial class MyForm : Form
{
private class MessageWriteRequestedEventArgs : EventArgs
{
public string Message { get; }
public MessageWriteRequestedEventArgs(string message)
{
Message = message;
}
}
private static event EventHandler<MessageWriteRequestedEventArgs> MessageWriteRequested;
public MyForm()
{
MessageWriteRequested += OnMessageWriteRequested;
}
public static void WriteMessage(string message)
{
MessageWriteRequested?.Invoke(this, new MessageWriteRequestedEventArgs(message));
}
private void OnMessageWriteRequested(object sender, MessageWriteRequestedEventArgs e)
{
richTextBox1.BeginInvoke(() => WriteMessageSafe(e.message));
}
private void WriteMessageSafe(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
// you're on ui context, you're safe to access local ui resources
WriteMessageSafe("This should be working!");
// if you have multiple MyForm instances, you need to use the event
WriteMessage("Broadcasting my tralala");
}
}
If you need to write to the textbox from anywhere else :
// do stuff
MyForm.WriteMessage("Ho Ho Ho !");
.NET already includes a class for reporting progress (or any other information) from an asynchronous operation in a thread-safe manner, Progress< T>. It doesn't need locking and even better, it decouples the sender and receiver. Many long-running BCL operations accept an IProgress<T> parameter to report progress.
You haven't explained what's going on in the form, or what task is reporting the data. Assuming the producer is another method in the same form, you could create a Progress<T> instance in the same method that starts the async operation, eg :
async void Button1_Click()
{
var progress=new Progress<string>(ReportMessage);
ReportMessage("Starting");
await Task.Run(()=>SomeLongOp(progress));
ReportMessage("Finished");
}
void SomeLongOp(IProgress<string> progress)
{
for(int i=0;i<1000000;i++)
{
...
progress.Report($"Message {i}");
...
}
}
void ReportMessage(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
By using IProgress< T>, the SomeLongOp method isn't tied to a specific form or global instance. It could easily be a method on another class
Publishing lots of messages
Let's say you have a lot of workers, doing a lot of things, eg monitoring a lot of devices, and want all of them to publish messages to the same Log textbox or RTF box. Progress< T> "simply" executes the reporting delegate or event handler on its original sync context. It doesn't have an asynchronous Report method, nor can it queue messages. In a really high-traffic environment, the synchronization switch can delay all workers.
The built-in answer to this is to use one of the pub/sub classes like ActionBlock< T> or a Channel.
An ActionBlock< T> processes the messages in its input queue in order, using a worker task that runs on the ThreadPool by default. This can be changed by specifying a different TaskScheduler in its execution options. By default, its input queue is unbounded.
One could use an ActionBlock to receive messages from multiple workers and display them on a textbox. The block can be created in the constructor, and passed to all workers as an ITargetBlock<T> interface :
ActionBlock<string> _logBlock;
public MyForm()
{
var options=new ExecutionDataFlowBlockOptions {
TaskScheduler=TaskScheduler.FromCurrentSynchronizationContext();
};
_block=new ActionBlock<string>(ReportMessage,options);
}
Now the fun begins. If the workers are created by the form itself, the workers can publish to the block directly :
public async void Start100Workers_Click(...)
{
var workers=Enumerable.Range(0,100)
.Select(id=>DoWork(id,_block));
await Task.WhenAll(workers);
}
async Task DoWork(int id,ITargetBlock<string> logBlock)
{
.....
await logBlock.SendAsync(message);
...
}
Or the block could be exposed through a public property, so other classes/forms in the application can post to it.
public ITargetBlock<string> LogBlock=>_block;
I'm going to show a simple way to do what I think you're after.
I started with a .NET Core 3.1 Win forms application. I added a rich text control to the form. I added a button to the form.
I added a TaskCompletionSource as a instance property - this will be used to control the tasks acting as workers which you described.
CancellationTokenSource sharedCancel = new CancellationTokenSource();
I created an interface to represent something that accepts messages as you described:
public interface IMyMessageSink
{
Task ReceiveMessage(string message);
}
I made my form support this interface.
public partial class Form1 : Form, IMyMessageSink
The ReceiveMessage method looks like this:
public Task ReceiveMessage(string message)
{
if(this.sharedCancel == null || this.sharedCancel.IsCancellationRequested)
return Task.FromResult(0);
this.Invoke(new Action<Form1>((s) => this.richTextBox1.Text = this.richTextBox1.Text.Insert(0, $"{message}\n")), this);
return Task.FromResult(0);
}
You'll see the Invoke handles the synchronization back to the UI thread.
This should probably use BeginInvoke and then convert the APM to async tasks which you can read about here. But for an SO answer the above simple code will suffice.
Also note there's no error handling. You'll want to add that to your generator and to the button handler.
Next I created a class to represent something that creates messages. This class takes the interface created and the cancellation token. It looks like this:
public class MyMessageGenerator
{
CancellationToken cancel;
IMyMessageSink sink;
public MyMessageGenerator(CancellationToken cancel, IMyMessageSink sink)
{
this.cancel = cancel;
this.sink = sink;
}
public async Task GenerateUntilCanceled()
{
try
{
while (!this.cancel.IsCancellationRequested)
{
await sink.ReceiveMessage(this.GetHashCode().ToString());
await Task.Delay(5000, this.cancel);
}
}
catch (OperationCanceledException)
{ }
}
}
In the button handler we create the message generators.
async void button1_Click(object sender, EventArgs e)
{
if (null == this.sharedCancel)
return;
await Task.Run(() => new MyMessageGenerator(this.sharedCancel.Token, this).GenerateUntilCanceled());
}
Finally I added an override for the form closing event:
protected override void OnClosing(CancelEventArgs e)
{
if (null != this.sharedCancel)
{
this.sharedCancel.Cancel();
this.sharedCancel.Dispose();
this.sharedCancel = null;
}
base.OnClosing(e);
}
If the application becomes larger and more complex you would likely benefit by adding services exposed using a DI container. You can read about adding DI to a winforms app here.
I have some code that takes a form object (winforms) and calls form.Show() on a dedicated thread (it spins a new thread).
This worked fine in Visual Studio 2008 (framework 3.5). I now migrated to 2010 and it fails with
InvalodOperationException: "Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
(of course half our files are checked out for the migration and the whole team is waiting...)
Here is some code (this is the running code so fogive me for some redundant level of detail):
private void ShowForm(object container)
{
FormContainer formContainer = (FormContainer)container;
Process sboProcess = GetSboProcess();
if (sboProcess != null)
{
WindowWrapper MyWindow = new WindowWrapper(sboProcess.MainWindowHandle);
if (formContainer.ShowDialog)
{
formContainer.Form.ShowDialog(MyWindow);
}
else
{
//formContainer.Form.Invoke((MethodInvoker)delegate() { formContainer.Form.Show(MyWindow); });
formContainer.Form.Show(MyWindow);
//Run thread while form is opened:
System.Windows.Forms.Application.Run(formContainer.Form);
}
}
}
public class FormContainer
{
private readonly Form form;
private readonly bool showDialog;
public FormContainer(Form form, bool showDialog)
{
this.form = form;
this.showDialog = showDialog;
}
public bool ShowDialog
{
get { return showDialog; }
}
public Form Form
{
get { return form; }
}
}
public class WindowWrapper : IWin32Window
{
private readonly IntPtr handle;
public WindowWrapper(IntPtr handle)
{
this.handle = handle;
}
#region Implementation of IWin32Window
/// <summary>
/// Gets the handle to the window represented by the implementer.
/// </summary>
/// <returns>
/// A handle to the window represented by the implementer.
/// </returns>
/// <filterpriority>1</filterpriority>
public IntPtr Handle
{
get { return handle; }
}
#endregion
}
Anyone with an idea?
Thanks,
Asher
In a sentence; the dedicated thread must call back to the UI thread that owns the form. Cross-thread UI operations are illegal, because there are certain operations that could cause the UI control involved to become detached from the Windows message pump. This is a bad thing because then Windows cannot tell the window to do anything, including draw itself, move, or even close. The window becomes "rogue", and all Windows can do is terminate the entire process that spawned the window.
The invocation of the form method can be a synchronous or asynchronous operation, but either way it has to be queued up to be run by the UI thread.
Here's a way to make calls from background threads SEEM like they're executed by the background thread, without violating cross-threading rules:
//in your form class
public new void Show()
{
if(!InvokeRequired)
base.Show();
else
this.Invoke((MethodInvoker)(()=>base.Show()));
}
Understand that you cannot deal with the form as its base class System.Windows.Form; it has to be the concrete form class you create. Otherwise the method hiding is ignored and the base implementation is used by default.
By setting it up this way, whenever an action might be about to run from a background thread, you avoid cross-threading operations. You can change this method to run asynchronously if desired by calling BeginInvoke() on the control instead of Invoke(), but as a call to Show() would normally run synchronously, I would stick with Invoke.
I'm writing an application in WPF. I have one main thread and another one - where I calculate something. In main thread I need to do one operation after additional thread will be finished. I can't use Join for additional thread, because I don't want to block main thread. How I can wait for finishing second thread and at the same time don't block main thread?
The eaisest way is to use the backgroundworker and handle the RunWorkerCompleted event.
I also invite you to take a look Part 3 of
Joseph Albahari's Threading in C# pdf
Another easy way is to use Task Parallel Library and chain multiple tasks with continuations.
Though it doesn't exempt you from #Conrad's advice: Read the threading book. It's fascinating and totally worth the efforts.
If you're creating your own threads, have the worker thread invoke a callback method when it's done:
public delegate void DoneDelegate (object calculationResults);
public class MyWorker
{
public DoneDelegate Done { get; set; }
public void Go()
{
object results = null;
// do some work
Done(results);
}
}
public class Main
{
public void StartWorker()
{
MyWorker worker = new MyWorker();
worker.Done = new DoneDelegate(DoneCallback);
System.Threading.Thread thread = new System.Threading.Thread(worker.Go);
thread.IsBackground = true;
thread.Start();
}
public void DoneCallback (object results)
{
// use the results
}
}
I have a class that I've created to allow asynchronous sequential execution of tasks, using the ThreadPool as the means of execution. The idea is that I'll have multiple instances running serial tasks in the background, but I don't want to have a separate dedicated Thread for each instance. What I'd like to check is whether this class is actually thread safe. It's fairly brief, so I thought I'd run it by the experts here, in case I'm missing something obvious. I've omitted a few of the convenience overloads for different Action types.
/// <summary>
/// This class wraps ThreadPool.QueueUserWorkItem, but providing guaranteed ordering of queued tasks for this instance.
/// Only one task in the queue will execute at a time, with the order of execution matching the order of addition.
/// This is designed as a lighter-weight alternative to using a dedicated Thread for processing of sequential tasks.
/// </summary>
public sealed class SerialAsyncTasker
{
private readonly Queue<Action> mTasks = new Queue<Action>();
private bool mTaskExecuting;
/// <summary>
/// Queue a new task for asynchronous execution on the thread pool.
/// </summary>
/// <param name="task">Task to execute</param>
public void QueueTask(Action task)
{
if (task == null) throw new ArgumentNullException("task");
lock (mTasks)
{
bool isFirstTask = (mTasks.Count == 0);
mTasks.Enqueue(task);
//Only start executing the task if this is the first task
//Additional tasks will be executed normally as part of sequencing
if (isFirstTask && !mTaskExecuting)
RunNextTask();
}
}
/// <summary>
/// Clear all queued tasks. Any task currently executing will continue to execute.
/// </summary>
public void Clear()
{
lock (mTasks)
{
mTasks.Clear();
}
}
/// <summary>
/// Wait until all currently queued tasks have completed executing.
/// If no tasks are queued, this method will return immediately.
/// This method does not prevent the race condition of a second thread
/// queueing a task while one thread is entering the wait;
/// if this is required, it must be synchronized externally.
/// </summary>
public void WaitUntilAllComplete()
{
lock (mTasks)
{
while (mTasks.Count > 0 || mTaskExecuting)
Monitor.Wait(mTasks);
}
}
private void RunTask(Object state)
{
var task = (Action)state;
task();
mTaskExecuting = false;
RunNextTask();
}
private void RunNextTask()
{
lock (mTasks)
{
if (mTasks.Count > 0)
{
mTaskExecuting = true;
var task = mTasks.Dequeue();
ThreadPool.QueueUserWorkItem(RunTask, task);
}
else
{
//If anybody is waiting for tasks to be complete, let them know
Monitor.PulseAll(mTasks);
}
}
}
}
UPDATE: I've revised the code to fix the main bugs kindly pointed out by Simon. This passes unit tests now, but I still welcome observations.
Don't do it. (Or at least avoid building your own stuff.)
Use the System.Threading.Tasks stuff (new in .NET 4.0). Create your a Task[] (size depends on number of parallel tasks you want) and let them read work items from a BlockingCollection while waiting for a CancellationToken. Your WaitForAll implementation would trigger your token, and call Task.WaitAll(Task[]) which will block until all your tasks are done.
Here's my second answer assuming that you cant use .NET 4.0 (and want comments on your existing code).
QueueTask enqueues the first task, getting isFirstTask = true, and starts a new thread. However, another thread may enqueue something while the first thread is processing, and Count == 0 => isFirstTask = true, and yet another thread is spawned.
Also, WaitUntilAllComplete will hang indefinitely if the task execution throws an exception (which may not necessarily crash everything, depending on exception handling), causing it to skip the call to RunNextTask().
And your WaitUntilAllComplete just waits until there are no more enqueue tasks, not that those currently executing are actually executing (they could just be enqueued in the ThreadPool) or complete.
It's built in in 4.0
How to: Create a Task Scheduler That Limits the Degree of Concurrency
You can also use a custom scheduler to achieve functionality that the default scheduler does not provide, such as strict first-in, first-out (FIFO) execution order. The following example demonstrates how to create a custom task scheduler. This scheduler lets you specify the degree of concurrency.
I see a few issues your with your SerialAsyncTasker class, but it sounds like you might have a good grasp of those so I will not go into any details on that topic (I may edit my answer with more details later). You indicated in the comments that you cannot use .NET 4.0 features nor can you use the Reactive Extensions backport. I propose that you use the producer-consumer pattern with a single consumer on a dedicated thread. This would perfectly fit your requirement of asynchronously executing tasks sequentially.
Note: You will have to harden the code to support gracefully shutting down, handling exceptions, etc.
public class SerialAsyncTasker
{
private BlockingCollection<Action> m_Queue = new BlockingCollection<Action>();
public SerialAsyncTasker()
{
var thread = new Thread(
() =>
{
while (true)
{
Action task = m_Queue.Take();
task();
}
});
thread.IsBackground = true;
thread.Start();
}
public void QueueTask(Action task)
{
m_Queue.Add(task);
}
}
Too bad you cannot use the BlockingCollection from the .NET 4.0 BCL or Reactive Extension download, but no worries. It is actually not too hard to implement one yourself. You can use Stephen Toub's blocking queue as a starting point and just rename a few things.
public class BlockingCollection<T>
{
private Queue<T> m_Queue = new Queue<T>();
public T Take()
{
lock (m_Queue)
{
while (m_Queue.Count <= 0) Monitor.Wait(m_Queue);
return m_Queue.Dequeue();
}
}
public void Add(T value)
{
lock (m_Queue)
{
m_Queue.Enqueue(value);
Monitor.Pulse(m_Queue);
}
}
}
public class ParallelExcecuter
{
private readonly BlockingCollection<Task> _workItemHolder;
public ParallelExcecuter(int maxDegreeOfParallelism)
{
_workItemHolder = new BlockingCollection<Task>(maxDegreeOfParallelism);
}
public void Submit(Action action)
{
_workItemHolder.Add(Task.Run(action).ContinueWith(t =>
{
_workItemHolder.Take();
}));
}
public void WaitUntilWorkDone()
{
while (_workItemHolder.Count < 0)
{
Monitor.Wait(_workItemHolder);
}
}
}
I need to implement threading to improve load time in a compact framework app. I want to fire off a background thread to do some calls to an external API, while the main thread caches some forms. When the background thread is done, I need to fire off two more threads to populate a data cache.
I need the background thread to be able to execute a callback method so I know it's done and the next two threads can be started, but the BeginInvoke method on a delegate is not supported in the compact framework, so how else can I do this?
You can arrange it yourself, simply make sure your thread method calls a completed method (or event) when it's done.
Since CF doesn't support the ParameterizedThreadStart either, I once made a little helper class.
The following is an extract and was not re-tested:
//untested
public abstract class BgHelper
{
public System.Exception Error { get; private set; }
public System.Object State { get; private set; }
public void RunMe(object state)
{
this.State = state;
this.Error = null;
ThreadStart starter = new ThreadStart(Run);
Thread t = new Thread(starter);
t.Start();
}
private void Run()
{
try
{
DoWork();
}
catch (Exception ex)
{
Error = ex;
}
Completed(); // should check Error first
}
protected abstract void DoWork() ;
protected abstract void Completed();
}
You are required to inherit and implement DoWork and Completed. It would probably make sense to use a < T> for the State property, just noticed that.
I know this is an old question, but if you are using CF 3.5 this would be a nice an short solution to the problem. Using lambda delegate..
ThreadStart starter = () => DoWorkMethodWithParams( param1, param2);
Thread myNewThread = new Thread(starter){IsBackground = true};
myNewThread.Start();