What is a correct way to queue complex tasks in wp8?
The tasks consists of the following:
Showing a ProgressIndicator through updating a model variable
Fetching or storing data to a wcf service (UploadStringAsync)
Updating potentially data bound model with the result from UploadStringCompleted.
Hiding the ProgressIndicator through updating a model variable
Currently I've been working with a class owning a queue of command objects, running a single thread that is started when an item is added if it's not already running.
I have however problems with waiting for tasks or subtasks where the code stops running.
Previously I've used async await, but a few levels down the behaviour was becoming more and more unpredictable.
What I want is the main thread being able to create and queue command objects.
The command objects should run one at a time, not starting a new one until the previous one is completely finished.
The command objects should be able to use the dispatcher to access the main thread if neccesary.
If you use async/await, there's no need for another thread (since you have no CPU-bound processing).
In your case, it sounds like you just need a queue of asynchronous delegates. The natural type of an asynchronous delegate is Func<Task> (without a return value) or Func<Task<T>> (with a return value). This little tip is unfortunately not well-known at this point.
So, declare a queue of asynchronous delegates:
private readonly Queue<Func<Task>> queue = new Queue<Func<Task>>();
Then you can have a single "top-level" task that just (asynchronously) processes the queue:
private Task queueProcessor;
The queueProcessor can be null whenever there's no more items. Whenever it's not null, it'll represent this method:
private async Task ProcessQueue()
{
try
{
while (queue.Count != 0)
{
Func<Task> command = queue.Dequeue();
try
{
await command();
}
catch (Exception ex)
{
// Exceptions from your queued tasks will end up here.
throw;
}
}
}
finally
{
queueProcessor = null;
}
}
Your Enqueue method would then look like this:
private void Enqueue(Func<Task> command)
{
queue.Enqueue(command);
if (queueProcessor == null)
queueProcessor = ProcessQueue();
}
Right now, I have the exception handling set up like this: any queued command that throws an exception will cause the queue processor to stop processing (with the same exception). This may not be the best behavior for your application.
You can use it like this (with either a lambda or an actual method, of course):
Enqueue(async () =>
{
ShowProgressIndicator = true;
ModelData = await myProxy.DownloadStringTaskAsync();
ShowProgressIndicator = false;
});
Note the use of DownloadStringTaskAsync. If you write TAP wrappers for your EAP members, your async code will be more "natural-looking" (i.e., simpler).
This is sufficiently complex that I'd recommend putting it into a separate class, but you'd want to decide how to handle (and surface) errors first.
Related
I'm trying to do some asynchronous I/O work detached from UI thread. Somewhere I read:
1) For CPU-bound code, you await an operation which is started on a
background thread with the Task.Run method. Such as calculating prime
numbers
2) For I/O-bound code, you await an operation which returns a
Task or Task inside of an async method. Such as waiting for
network or database
So I did this:
// in windows form UI
private async void btnImport_Click(object sender, EventArgs e) {
// [...]
List<DataRow> rows = await importer.ImportDataAsync(123, 456);
// [...]
}
// in Importer.ImportDataAsync:
public async Task<List<DataRow>> ImportDataAsync(int parent, int child, CancellationToken token = default(CancellationToken)) {
// [...]
List<DataRow> list = await RealImportFromDB(parent, child);
return list;
// [...]
}
public List<DataRow> RealImportFromDB(int p, int c) {
List<DataRow> rowList;
// here fetch the rows from DB over slow network
// and return the row list
return rowList;
}
With this approach the UI is blocked.
If I call RealImportFromDB(...) like this
List<DataRow> l = await Task.Run(() => RealImportFromDB(parent, child));
the UI is not blocked but that conflicts with point 2) from above IMHO.
Where do I do things wrong?
Best regards, alex
public List<DataRow> RealImportFromDB(int p, int c) is a blocking call to the database, so to execute it Asynchronously, you have used the #1, where you have wrapped the call inside the Task.Run, which will free up Ui thread as expected
With this approach the UI is blocked. If I call RealImportFromDB(...)
It is since the method is not meant for the Asynchronous calling, it doesn't return a Task or Task<T>, which is the common requirement to make the Async call
Your code, await RealImportFromDB(parent, child) is not correct that's a compilation error, since you can only await the calls, which implements the GetAwaiter()internally check (this and this) and most common scenario is to return Task or Task<T>, there are other types too
Let's try to understand your two statements:
1) For CPU-bound code, you await an operation which is started on a background thread with the Task.Run method. Such as calculating prime numbers
This is what you are currently doing and is often done in clients to free up Ui thread, while processing takes place in background, but this would still use a Thread pool thread to do the execution, which is not as important as Ui thread, but still system resource
2) For I/O-bound code, you await an operation which returns a Task or Task inside of an async method. Such as waiting for network or database
To implement this you need a method, which is Async by default and returning a Task or Task<T>, such methods are part of all data frameworks, for every sync method nowadays there's a corresponding async method to initiate a asynchronous execution and they are the true IO calls, where they don't use a thread, since the processing is not in the same process, its across network / process boundary, so calling thread needn't wait, it just needs to come back and pick the result, when it arrives (any thread pool thread, not necessary the dispatching thread). Internally such methods use TaskCompletionSource<T> (When to use TaskCompletionSource), which has mechanism to notify the caller when the network call has accomplished
To implement this you need a method, which is Async by default and
returning a Task or Task
Thanks a lot, this was my trouble. My RealImportFromDB(...) method is not an async method since it deals with an older, proprietary library that seems not ready for async calls.
Those were my thougths:
with awaiting the result from ImportDataAsync(...) everything that is called within (e.g. RealImportFromDB(...)) is dispatched from the UI thread also. So to say: everything within ImportDataAsync(...) is encapsulated / runs on in the second, non-blocking thread.
#others: yes you are right, the sample from my code won't even compile. Fiddled around a lot, so the code sample does not show everything what was changed, sorry for that :-}
I am trying to get the proper 'structure' for monitoring the state of a game from external source(s) using (Tasks) async/await in order to run the tasks in an infinite loop, however the current way its written seems to just freeze up my UI.
What I have so far:
(in the "state machine" class)
// Start monitoring the game state for changes
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IsRunning = true;
task = Task.Factory.StartNew(async () =>
{
while (true)
{
await Task.Run(()=>CheckForStateChange());
await Task.Delay(1000); // Pause 1 second before checking state again
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
}
Without the above "Task.Delay" line the UI completely freezes up. With the "Task.Delay" line it doesn't freeze up, but if I try to drag the window it skips back to where I began dragging it.
My assumption with the current code is that the 'await Task.Run()' executes and upon completion the 'await Task.Delay()' executes and then on completion returns to the beginning of the while(true) infinite loop. (ie. not running in parallel).
The CheckForStateChange() signature is as follows:
private void CheckForStateChange()
{
// ... A bunch of code to determine and update the current state value of the object
}
Nothing special there, simple non-async method. I have read through lots of examples / questions here on StackOverflow and I used to have CheckForStateChange as returning a Task (with awaitable actions inside the method) and many other iterations of code (with the same results).
Finally I call the Start() method from the main win32 form (button) as follows:
private void btnStartSW_Click(object sender, EventArgs e)
{
// Start the subscription of the event handler
if(!state.IsRunning)
{
state.StateChange += new SummonersWar.StateChangeHandler(OnGameStateChange);
state.Start();
}
}
I think the above code is the simplest form I have written the code structure in so far, but apparently its still not written 'properly'. Any help would be appreciated.
UPDATE:
The publisher side (state machine class):
// ------ Publisher of the event ---
public delegate void StateChangeHandler(string stateText);
public event StateChangeHandler StateChange;
protected void OnStateChange() // TODO pass text?
{
if (StateChange != null)
StateChange(StateText());
}
Where the StateText() method is just a temporary way of retrieving a 'text' representation of the current state (and is really a placeholder at this point until I organize it into a tidier struct)
IsRunning is purely a public bool.
And the handler in the UI thread:
private void OnGameStateChange(string stateText)
{
// Game State Changed (update the status bar)
labelGameState.Text = "State: " + stateText;
}
Why the UI freezes
In terms of the main question: you're already calling your CheckForStateChange via Task.Run, so there is no way that your CheckForStateChange will freeze the UI unless it includes calls which are marshalled back to the UI thread (i.e. Control.Invoke or SynchronizationContext.Post/Send used explicitly, or implicitly via a Task started on the UI TaskScheduler).
The best place to start looking is your StateChange handlers (i.e. StateChangeHandler). Also have a look at where the StateChange event is raised. You'll find thread marshalling code at one of these sites.
Other issues
You're passing the TaskScheduler pointing to the UI SynchronizationContext to the outer task. You're also passing in TaskCreationOptions.LongRunning. In simple terms you're telling the task factory to "start a task on a dedicated thread, and on the current thread". These two are mutually exclusive requirements and you can pretty safely drop them both.
If, as a result of the above, your outer task happens to execute on the UI thread, it won't really trip you up as the inner call is wrapped in Task.Run, but this probably isn't the behaviour you expect.
You are storing the result of Task.Factory.StartNew inside a task field or property. Note, however, that your Task.Factory.StartNew call returns a Task<Task>, so the saved Task instance will transition to completed state almost immediately unless you call Unwrap on it and get to the inner task. To avoid this entire mess, just use Task.Run to create the outer task (as it has Unwrap semantics built in). If you do that, you can ditch the inner Task.Run completely, like so:
public bool IsRunning
{
get
{
return task.Status == TaskStatus.Running;
}
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
task = Task.Run(async () =>
{
while (true)
{
CheckForStateChange(token);
token.ThrowIfCancellationRequested();
await Task.Delay(1000); // Pause 1 second before checking state again
}
}, token);
// Uncomment this and step through `CheckForStateChange`.
// When the execution hangs, you'll know what's causing the
// postbacks to the UI thread and *may* be able to take it out.
// task.Wait();
}
Since you have a CancellationToken you need to be passing it to CheckForStateChange, and checking it periodically - otherwise it only gets checked once, when the Task is started, and then never again.
Note that I have also provided a different IsRunning implementation. Volatile state is hard to get right. If the framework is giving it to you for free, you should use it.
Final word
Overall this entire solution feels like a bit of a crutch for something that should be done more reactively - but I can think of scenarios where this sort of design is valid. I'm just not convinced that yours is really one of them.
EDIT: how to find what's blocking the UI
I'll get downvoted to oblivion for this, but here goes:
The sure way to find what's causing postbacks to the UI thread is to deadlock with it. There's plenty of threads here on SO telling you how to avoid that, but in your case - we'll cause it on purpose and you'll know exactly what calls you need to avoid when you're polling for changes - although whether or not it will be possible to avoid these calls, remains to be seen.
I've put a task.Wait instruction at the end of my code snippet. Provided that you call Start on the UI thread, that should cause a deadlock with something inside your CheckForStateChange, and you will know what it is that you need to work around.
First of all, let me describe the flow of the program I am writing.
There is a list of objects in the GUI and when the user clicks on one, the object data is read from the disk and is loaded. This could take around 3-4 seconds.
Let's say the user is impatient and clicks on another object while the first one is still loading. The program will load the second object and at the same time, will cancel the loading for the first object.
As the user could spam load operations before any of them could complete successfully, I have implemented an action block to queue up all the load operations.
So I have an action block like this:
this.loadObjectActionBlock = new ActionBlock<CaseObject>(
c => this.LoadCaseData(CaseObject),
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 10,
});
Every time the user has clicked on a Case Object, I will call:
this.loadObjectActionBlock.Post(CaseObject);
And the CaseObject will be processed by a function I have defined like this:
private void LoadCaseData(CaseObject caseObject)
{
caseObject.LoadCaseData();
}
What I need to do right now is, I need to wait until ALL CaseObjects are loaded so that I could continue my code after that.
I have tried detecting when all cases are processed by calling
if (this.loadObjectActionBlock.InputCount == 0)
{
this.loadObjectActionBlock.Complete();
}
after caseObject.LoadCaseData(); but this leads to strange results when load actions happen way too fast and the action block is told not to accept any more inputs. If I understand correctly, the InputCount property only looks at the number of jobs left in the queue.
So what I'd like to do is I'd like to await the ActionBlock like:
await this.loadObjectActionBlock.Completion;
when everything in the queue has been fully processed.
I may not be using the ActionBlock as it is intended to, so if there are any alternatives on this, please kindly suggest anything that I could read up on.
TLDR: I'd like to process multiple tasks (started by user) concurrently and wait for all to be complete then followed by a single task.
Thanks in advance :)
The program will load the second object and at the same time, will cancel the loading for the first object.
A queue is not the appropriate solution for this behavior, especially since a TPL block can only be completed once.
If you want to implement this behavior, simply ensure the cancellation token is observed before continuing with the next operation:
private static void ProcessCase(CaseObject caseObject, CancellationToken token)
{
caseObject.LoadCaseData();
token.ThrowIfCancellationRequested();
... // Further processing goes here
}
Called from the UI thread as:
static CancellationTokenSource _cts;
private static async Task ProcessCaseAsync(CaseObject caseObject)
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
await Task.Run(() => ProcessCase(caseObject, _cts.Token));
}
I am attempting to use a BackgroundWorker to complete a task. I have got the worker to run correctly, under the DoWork method it then calls another method which executes but then I face my problem: when that method tries to call another method it does not succeed and does not throw an exception and I can only see this as being something I am doing wrong with BackgroundWorker since when running on the UI thread for testing the methods execute as intended.
Here is where I run my worker:
private void btnAddShots_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Here is my DoWork method:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
Here is a stripped down version of addTaskPair method that is called by my worker:
private void addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
Task trackingTask = new Task();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json");
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
Within the above sample you will see that I call the method postJSON, this is where I hit my wall. Through testing I have verified that the above method runs, but the postJSON method does not run at all when being called from within this thread.
I have seen many references to Invoking when researching this problem but they all seem to apply to changing ui controls which I do not need to do (though a progress bar is manipulated using the ProgressChanged BackgroundWorker event).
I can clarify my problem more if needs be but I am really hoping for assistance with this since I have never successfully worked with backgroundworker or threading before (I am not a professional as I'm sure you can tell from my code).
You're working with the UI controls from the BackgroundWorker.DoWork event handler. Don't do that.
Collect the data before starting the BackgroundWorker, and pass it as an argument to the RunWorkerAsync method. Do not touch the UI from the BackgroundWorker.DoWork - progress updates through the ReportProgress method are fine.
Also, if you're running on .NET 4.5+, you might want to consider using the new Task pattern instead. It still requires you to collect the data to process beforehand, but it's a lot easier to work with:
(EDIT: As Peter suggested, the invalid access is happening in cmbTrackingList.SelectedIndex; I've included it in the code below. That's exactly the reason I suggested using static methods for the operations happening in a separate thread - it makes you think a lot more about the data you're working with)
var todoList = todoLists.todoLists[cmbTrackingList.SelectedIndex];
var data =
dataGridShots
.Rows
.Select
(
i =>
new
{
TaskName = i.Cells[0].Value.ToString(),
TaskDescription = i.Cells[1].Value.ToString(),
TaskPriority = i.Cells[2].Value.ToString()
}
)
.ToArray();
var result =
await Task.Run
(
() =>
foreach (var row in data)
handleRowData(row.TaskName, row.TaskDescription, row.TaskPriority, todoList)
);
Now that you're this far, you might notice that it shouldn't be too hard to make your postJson method asynchronous as well (there's plenty of ways to make HTTP requests asynchronously) - this will allow you to make your whole code asynchronous without blocking any thread.
Multi-threading is hard. Always try to work with the highest possible abstraction, and avoid any shared state. If you do need shared state, you need to synchronize every single access to it, from each of the threads - try to avoid that (a good practice being having the methods executing on different threads static, so that you don't accidentaly touch shared state).
From your description, it seems that the access of the DataGrid components is not the problem. That is, it seems those statements get executed correctly, and the addTaskPair() method is successfully called, but the postJSON() method is not.
Given that, I suspect that the evaluation of cmbTrackingList.SelectedIndex is what's throwing an exception and interrupting the thread.
That said, the advice is still the same: keep the UI-related stuff on the UI thread, and only run the other stuff outside the UI thread. Given the code you've posted, it seems likely that the only thing that really ought to be asynchronous (i.e. run in the background, so as to not delay the UI thread too much) is the call to postJSON(). Presumably this is a synchronous network call, and so could take awhile. The other stuff should run deterministically and quickly.
Given that, here's how I'd refactor the code, taking advantage of the new async/await feature:
private async void btnAddShots_Click(object sender, EventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
await addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
private async Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
jsonResponse = await Task.Run(() => postJSON(trackingJson, url));
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
NOTE: in the above I changed the name of your own Task type to TaskData. I strongly recommend you pick a name other than Task, due to the pervasive use of the .NET Task type throughout the modern .NET API.
In the above, most of the code will run on the UI thread. The async methods are rewritten by the compiler to return at any await statement, and to resume execution of that method when the awaited Task has completed. Note that the async method only returns when there is finally a Task object to await; so in the above, the btnAddShots_Click() method will initially return once the addTaskPair() method has called Task.Run() and itself has returned at the await statement.
Important: in this context, calling and awaiting an asynchronous method from the UI thread causes the framework to run the rest of the method back on the UI thread. That is, when the asynchronous operation has completed, control of the execution of the code is returned back to the UI thread where you started.
It's this feature that makes all of this work correctly, so it's useful to make sure you understand it. :)
The call to postJSON() is executed in a separate thread, using the Task object that is created by the Task.Run() method. Since that will be executing on other than the UI thread, I've moved the computation of its URL argument to a local variable just before the call to Task.Run(), which variable is then passed to the postJSON() method when it's called in the task thread. Doing this ensures that the evaluation of cmbTrackingList.SelectedIndex is done in the UI thread.
EDIT:
Noting that the OP has commented he is using .NET 4 and not 4.5 (in which the async/await feature was officially released), I offer this slightly more awkward alternative, which still preserves the execution characteristics of the preferable 4.5-compatible version above. While one can install the async/await features on VS2010 (and IMHO that's a better way to go), this alternative allows for "pure" .NET 4 code while still achieving basically the same runtime result.
private void btnAddShots_Click(object sender, EventArgs e)
{
Action<Task> continuation = null;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
// Note that the continuation delegate is chained, attaching itself as
// the continuation for each successive task, thus achieving a looping
// mechanism.
continuation = task =>
{
if (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString())
.ContinueWith(continuation, uiScheduler);
count += 1;
}
}
// Invoking the continuation delegate directly gets the ball rolling
continuation(null);
}
private Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
// NOTE: must explicitly specify TaskScheduler.Default, because
// the default scheduler in the context of a Task is whatever the
// current scheduler is, which while executing a continuation would
// be the UI scheduler, not TaskScheduler.Default.
return Task.Factory.StartNew(() => postJSON(trackingJson, url),
CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(task =>
{
if (task.Exception != null)
{
// Task exceptions are wrapped in an AggregateException
debugMessage(task.Exception.InnerException.ToString());
}
else
{
string jsonResponse = task.Result;
// do something with jsonResponse?
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
Notes:
The task scheduler for the current synchronization context is used for each continuation. This ensures that the continuation itself is executed back on the UI thread, where you can safely interact with UI objects.
The most awkward part of this IMHO is that your while loop becomes an if statement, since the execution of the loop spans multiple method invocations.
While async/await would normally allow normal exception-handling syntax, when using explicit continuations you don't have this option. But Task will wrap any exceptions that occur in an AggregateException instance, which you can use to get at the real exception and report it.
TaskFactory.StartNew() has a somewhat subtle behavior: the scheduler it uses to run the given task is TaskScheduler.Current. The first time the addTaskPair() method is called, there is no current task, and so TaskScheduler.Current returns the default (i.e. thread pool) scheduler. But every subsequent time the addTaskPair() method is called, it is from within a task continuation, and so TaskScheduler.Current would return the scheduler being used to execute the continuation. Of course, we've intentionally made this the UI scheduler and running the new postJSON() task using that scheduler would defeat the purpose, as it would just execute synchronously on the current thread. So it's imperative here to specify the scheduler we want, i.e. TaskScheduler.Default, corresponding to the thread pool scheduler.
Without async/await, it's a bit harder to get things just right. It's syntactically more verbose, but IMHO it's still a reasonably good option, as it substantially preserves the desired, imperative structure of the code. In particular, you get to keep the flow of execution in the UI thread, making access of UI objects trivial, and forking off the long-running operation(s) only as needed.
(I should also point out that this .NET 4 version is not strictly exactly what the compiler would generate for you when using async/await. It's very similar, but not quite the same. Also, I will point out that while implementing a method with a single await in this way is not too bad, it gets a bit out of hand if you want multiple continuations in the same method. It's possible, but at that point I'd think the urge to just upgrade to the latest version of C# would be very compelling :) ).
Finally…
If after all of the above, you want to stick with BackgroundWorker, it should be possible to avoid the exception that's occurring with a relatively simple change to your code:
int selectedIndex = (int)Invoke((Func<int>)(() => cmbTrackingList.SelectedIndex));
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[selectedIndex].id + "/tasks.json");
I.e. just use the Control.Invoke() method to invoke on the UI thread an anonymous method that will return the value of cmbTrackingList.SelectedIndex. The Control.Invoke() method will receive the returned value, and will in turn return it to you. Since Control.Invoke() is general-purpose, its object return type must be cast to the type you know is being returned.
This ensures that the cmbTrackingList object is accessed only on the UI thread. I'll also note that if this index is not expected to change while the background processing is going on (or is specifically not supposed to), then yet another alternative would be to retrieve the value in your btnAddShots_Click() method and then pass it down to the DoWork event handler, which in turn would pass it to the addTaskPair() method where it's needed.
I put this option last because I really believe that learning the async/await feature is important and worthwhile, and that while the BackgroundWorker class has served us well over the years, it's essentially been deprecated by the new features. But I also readily admit that BackgroundWorker is still a fine way to do things, and can be made to work in your scenario.
I am trying to do the following :
I have a server that is supposed to get many messages from a queue and process them. Now what I want is to create a new thread for every message and those threads will handle the response to the queue, I just want my server (core thread) to be just listening to messages and creating threads, not caring of what happens to them.
How can I achieve this? I know I can use the Thread class to create a thread but then the application just keeps listening to the thread until if finishes.
Also I can create an async method and run it but what happens when it finishes? Also the method is supposed to be static if I want it to be async but in my current application that is not a solution since I use many non static variables into this method.
Any ideas would be appreciated.
Unless you have very specific reason, I'd recommend using Tasks instead of Threads.
Likely they'll run in background anyway, but they produce less CPU/memory overhead and (in my opinion) are easier to handle in case of exception,...
Task t = Task.Run(() => ProcessMessage(message));
Maybe take a look at this introduction
What do you mean with
I know I can use the Thread class to create a thread but then the application just keeps listening to the thread until if finishes.
Just spawn the thread and let it run:
{
Thread t = new Thread(Foo);
t.Start();
}
public void Foo()
{ }
This won't make the main thread listen to the child thread, it just spawn them and continue working on following instructions.
BTW there are tons of result on how to create and run threads.
Since I don't like when others do it, here are simple examples of each way (asynchrnous/task-based), and you pick which one you like.
Asynchronous Implementation
int main()
{
while(true)
{
string data = SomeMethodThatReturnsTheNextDataFromQueue();
ProcessDataAsync(data);
}
}
async private void ProcessDataAsync(string msg)
{
// The *await* keyword returns to caller and allows main thread to continue looping.
bool result = await ParseDataAndSaveSomewhere(msg);
return;
}
Task-Based Implementation
int main()
{
while(true)
{
string data = SomeMethodThatReturnsTheNextDataFromQueue();
Task task = new Task(() => { ProcessData(data) });
task.Start();
}
}
private void ProcessData(string data)
{
// Do work
}