detect when each task is complete - c#

I want to update a progressbar as each task is completed below.
The method var continuation2 = Task.Factory.ContinueWhenAny(..... doesnt work.
What is the correct way to do this?
C# Code
private void radButtonInsertManyErrors_Click(object sender, EventArgs e)
{
try
{
radProgressBarStatus.Maximum = int.Parse(radTextBoxNumberofErrorsInsert.Text);
radProgressBarStatus.Value1 = 0;
Task<int>[] tasks = new Task<int>[int.Parse(radTextBoxNumberofErrorsInsert.Text)];
for (int i = 0; i < int.Parse(radTextBoxNumberofErrorsInsert.Text); i++)
{
int x = i;
tasks[i] = new Task<int>(() =>
{
//insert the error into table FA_Errors
Accessor.Insert_FAErrors(BLLErrorCodes.BLL_Error_Codes.Error_Log_Event_Login.ToString(),
(int)BLLErrorCodes.BLL_Error_Codes.Error_Log_Event_Login,
"Some Error", "",
MethodBase.GetCurrentMethod().DeclaringType.Namespace.ToString(),
MethodBase.GetCurrentMethod().Name.ToString(),
BLLErrorCategory.BLL_Error_Category.WEB_APP.ToString(),
"pc source", "damo",
sConn.ToString());
return 1;
});
}
var continuation = Task.Factory.ContinueWhenAll(
tasks,
(antecedents) =>
{
RadMessageBox.Show("Finished inserting errors ");
});
var continuation2 = Task.Factory.ContinueWhenAny(
tasks,
(antecedents) =>
{
radProgressBarStatus.Value1++;
});
for (int i = 0; i < int.Parse(radTextBoxNumberofErrorsInsert.Text); i++)
tasks[i].Start();
// Use next line if you want to block the main thread until all the tasks are complete
//continuation.Wait();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}

You can use this function:
public static void TaskProgress(IEnumerable<Task> tasks, Action<int> callback)
{
int count = 0;
foreach (var task in tasks)
task.ContinueWith(t => callback(Interlocked.Increment(ref count)));
}
It will call the callback each time a task completes with the number of currently completed tasks. Note that the callbacks are not synchronized, so it can be called while the previous callback is still running.

Set up a continuation with each of the tasks. Keep a (thread-safe) counter on how many completed and update the UI on completion of each task.
Actually, Task.WhenAll does keep such a counter under the hood. It is just not accessible.

Related

establish a condition in async form in c#

I have a problem that is when I call an async method, it enters and comes up against a condition that I have set, where it sends me an exception that says:
"The subprocess making the call can not access this object because the
owner is another thread.",
I just want the condition process to run in the background with the async method
private Task ObtenerDatosd()
{
return Task.Run(() =>
{
for (int i = draw2.Count - 1; i >= 0; i--)
{
if (draw2[i].ToString().ToLower().Contains(SearcInterno.Text.ToLower()))
{
//action
System.Windows.MessageBox.Show("Code action");
}
}
});
}
You cannot access WPF objects from another thread without a dispatcher. But you don't need it in this case, if you just use:
private Task ObtenerDatosd()
{
string text = SearcInterno.Text.ToLower();
return Task.Run(() =>
{
for (int i = draw2.Count - 1; i >= 0; i--)
{
if(draw2[i].ToString().ToLower().Contains(text))
{
//action
System.Windows.MessageBox.Show("Code action");
}
}
});
}

Create a multi threaded applications to run multiple queries in c#

I'm trying to build a Windows Forms tool that runs queries asynchronously.
The app has a datagridview with 30 possible queries to run. The user checks the queries he wants to execute, say 10 queries, and hits a button.
The app has a variable called maxthreads = 3 (for the sake of discussion) that indicates how many threads can be used to async run the queries. The queries run on a production environment and we don't want to overload the system with too many threads running in the same time. Each query runs for an average of 30 sec. (some 5 min., others 2 sec.)
In the datagridview there is an image column containing an icon that depicts the status of each query (0- Available to be run, 1-Selected for running, 2- Running, 3- Successfully completed, -1 Error)
I need to be able to communicate with the UI every time a query starts and finishes. Once a query finishes, the results are being displayed in a datagridview contained in a Tabcontrol (one tab per query)
The approach: I was thinking to create a number of maxthread backgroundworkers and let them run the queries. As a backgroundworker finishes it communicates to the UI and is assigned to a new query and so on until all queries have been run.
I tried using an assignmentWorker that would dispatch the work to the background workers but don't know how to wait for all threads to finish. Once a bgw finishes it reports progress on the RunWorkerCompleted event to the assignmentWorker, but that one has already finished.
In the UI thread I call the assignment worker with all the queries that need to be run:
private void btnRunQueries_Click(object sender, EventArgs e)
{
if (AnyQueriesSelected())
{
tcResult.TabPages.Clear();
foreach (DataGridViewRow dgr in dgvQueries.Rows)
{
if (Convert.ToBoolean(dgr.Cells["chk"].Value))
{
Query q = new Query(dgr.Cells["ID"].Value.ToString(),
dgr.Cells["Name"].Value.ToString(),
dgr.Cells["FileName"].Value.ToString(),
dgr.Cells["ShortDescription"].Value.ToString(),
dgr.Cells["LongDescription"].Value.ToString(),
dgr.Cells["Level"].Value.ToString(),
dgr.Cells["Task"].Value.ToString(),
dgr.Cells["Importance"].Value.ToString(),
dgr.Cells["SkillSet"].Value.ToString(),
false,
new Dictionary<string, string>()
{ { "#ClntNb#", txtClntNum.Text }, { "#Staff#", "100300" } });
qryList.Add(q);
}
}
assignmentWorker.RunWorkerAsync(qryList);
}
else
{
MessageBox.Show("Please select at least one query.",
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
}
Here is the AssignmentWorker:
private void assignmentWorker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (Query q in (List<Query>)e.Argument)
{
while (!q.Processed)
{
for (int threadNum = 0; threadNum < maxThreads; threadNum++)
{
if (!threadArray[threadNum].IsBusy)
{
threadArray[threadNum].RunWorkerAsync(q);
q.Processed = true;
assignmentWorker.ReportProgress(1, q);
break;
}
}
//If all threads are being used, sleep awhile before checking again
if (!q.Processed)
{
Thread.Sleep(500);
}
}
}
}
All bgw run the same event:
private void backgroundWorkerFiles_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Query qry = (Query)e.Argument;
DataTable dtNew = DataAccess.RunQuery(qry).dtResult;
if (dsQryResults.Tables.Contains(dtNew.TableName))
{
dsQryResults.Tables.Remove(dtNew.TableName);
}
dsQryResults.Tables.Add(dtNew);
e.Result = qry;
}
catch (Exception ex)
{
}
}
Once the Query has returned and the DataTable has been added to the dataset:
private void backgroundWorkerFiles_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
try
{
if (e.Error != null)
{
assignmentWorker.ReportProgress(-1, e.Result);
}
else
{
assignmentWorker.ReportProgress(2, e.Result);
}
}
catch (Exception ex)
{
int o = 0;
}
}
The problem I have is that the assignment worker finishes before the bgw finish and the call to assignmentWorker.ReportProgress go to hell (excuse my French).
How can I wait for all the launched bgw to finish before finishing the assignment worker?
Thank you!
As noted in the comment above, you have overcomplicated your design. If you have a specific maximum number of tasks (queries) that should be executing concurrently, you can and should simply create that number of workers, and have them consume tasks from your queue (or list) of tasks until that queue is empty.
Lacking a good Minimal, Complete, and Verifiable code example that concisely and clearly illustrates your specific scenario, it's not feasible to provide code that would directly address your question. But, here's an example using a List<T> as your original code does, which will work as I describe above:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TestSO42101517WaitAsyncTasks
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
int maxTasks = 30,
maxActive = 3,
maxDelayMs = 1000,
currentDelay = -1;
List<TimeSpan> taskDelays = new List<TimeSpan>(maxTasks);
for (int i = 0; i < maxTasks; i++)
{
taskDelays.Add(TimeSpan.FromMilliseconds(random.Next(maxDelayMs)));
}
Task[] tasks = new Task[maxActive];
object o = new object();
for (int i = 0; i < maxActive; i++)
{
int workerIndex = i;
tasks[i] = Task.Run(() =>
{
DelayConsumer(ref currentDelay, taskDelays, o, workerIndex);
});
}
Console.WriteLine("Waiting for consumer tasks");
Task.WaitAll(tasks);
Console.WriteLine("All consumer tasks completed");
}
private static void DelayConsumer(ref int currentDelay, List<TimeSpan> taskDelays, object o, int workerIndex)
{
Console.WriteLine($"worker #{workerIndex} starting");
while (true)
{
TimeSpan delay;
int delayIndex;
lock (o)
{
delayIndex = ++currentDelay;
if (delayIndex < taskDelays.Count)
{
delay = taskDelays[delayIndex];
}
else
{
Console.WriteLine($"worker #{workerIndex} exiting");
return;
}
}
Console.WriteLine($"worker #{workerIndex} sleeping for {delay.TotalMilliseconds} ms, task #{delayIndex}");
System.Threading.Thread.Sleep(delay);
}
}
}
}
In your case, each worker would report progress to some global state. You don't show the ReportProgress handler for your "assignment" worker, so I can't say specifically what this would look like. But presumably it would involve passing either -1 or 2 to some method that knows what to do with those values (i.e. what would otherwise have been your ReportProgress handler).
Note that the code can simplified somewhat, particularly where the individual tasks are consumed, if you use an actual queue data structure for the tasks. That approach would look something like this:
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace TestSO42101517WaitAsyncTasks
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
int maxTasks = 30,
maxActive = 3,
maxDelayMs = 1000,
currentDelay = -1;
ConcurrentQueue<TimeSpan> taskDelays = new ConcurrentQueue<TimeSpan>();
for (int i = 0; i < maxTasks; i++)
{
taskDelays.Enqueue(TimeSpan.FromMilliseconds(random.Next(maxDelayMs)));
}
Task[] tasks = new Task[maxActive];
for (int i = 0; i < maxActive; i++)
{
int workerIndex = i;
tasks[i] = Task.Run(() =>
{
DelayConsumer(ref currentDelay, taskDelays, workerIndex);
});
}
Console.WriteLine("Waiting for consumer tasks");
Task.WaitAll(tasks);
Console.WriteLine("All consumer tasks completed");
}
private static void DelayConsumer(ref int currentDelayIndex, ConcurrentQueue<TimeSpan> taskDelays, int workerIndex)
{
Console.WriteLine($"worker #{workerIndex} starting");
while (true)
{
TimeSpan delay;
if (!taskDelays.TryDequeue(out delay))
{
Console.WriteLine($"worker #{workerIndex} exiting");
return;
}
int delayIndex = System.Threading.Interlocked.Increment(ref currentDelayIndex);
Console.WriteLine($"worker #{workerIndex} sleeping for {delay.TotalMilliseconds} ms, task #{delayIndex}");
System.Threading.Thread.Sleep(delay);
}
}
}
}

Task doesn't run in parallel

I have a task, but processes inside it don't run in parallel. Second one waits for the first one to compelete. Can you explain why and how can I correct this? I want both of them run at the same time.
And second question, Should I use task instead of threads?
Thanks in advance.
new Task(() =>
{
Counter1();
Counter2();
}).Start();
private void Counter2()
{
for (int i = 0; i < 30; i++)
{
Thread.Sleep(500);
label2.Text = i.ToString();
}
}
private void Counter1()
{
for (int i = 0; i < 30; i++)
{
Thread.Sleep(500);
label3.Text = i.ToString();
if(i == 15)
Thread.Sleep(3000);
}
}
Use Parallel.Invoke and call Counter1() and Counter2() as shown in following example from MSDN (after updating the () anonymous lambda invocation to invoke your 2 methods.
#region ParallelTasks
// Perform three tasks in parallel on the source array
Parallel.Invoke(() =>
{
Console.WriteLine("Begin first task...");
GetLongestWord(words);
}, // close first Action
() =>
{
Console.WriteLine("Begin second task...");
GetMostCommonWords(words);
}, //close second Action
() =>
{
Console.WriteLine("Begin third task...");
GetCountForWord(words, "species");
} //close third Action
); //close parallel.invoke
Console.WriteLine("Returned from Parallel.Invoke");
#endregion

Aggregate and forward exceptions from multiple threads

In my method I start multiple threads and then wait until they finish their work (something like fork-join pattern).
using (var countdownEvent = new CountdownEvent(runningThreadsCount))
{
for (int i = 0; i < threadsCount; i++)
{
var thread = new Thread(new ThreadStart(delegate
{
// Do something
countdownEvent.Signal();
}));
thread.Start();
}
countdownEvent.Wait();
}
Now I need to be able to catch exception in this threads (lets assume that // Do something may throw an exception), pass exception to the main thread, unblock it (since it is waiting on countdownEvent) and re-throw the exception.
What is the most elegant way to achieve that?
Solved my problem with Tasks API. Thanks flq for suggestion!
var cancellationTokenSource = new CancellationTokenSource();
var tasks = new Task[threadsCount]
for (int i = 0; i < threadsCount; i++)
{
tasks[i] = Task.Factory.StartNew(
delegate
{
// Do something
}, cancellationTokenSource.Token);
}
try
{
Task.WaitAll(tasks);
}
catch (AggregateException ae)
{
cancellationTokenSource.Cancel();
throw ae.InnerExceptions[0];
}

very unique exception for BlockingCollection on .net 4.0

i'm using the BlockingCollection for a Producer Consumer pattern and i got an excecption i think to write a patent on it- only two results in google!
the expection is "CompleteAdding may not be used concurrently with additions to the collection" and it happens when i TryAdd on th BlockingCollection as Follows:
public void EnqueueTask(T item)
{
if (!_cancellationTokenSource.IsCancellationRequested)
{
_workerQueue.Add(item);
}
}
the CompleteAdding is called on the dispose of the Consumer-Producer wrapper class:
public void Dispose()
{
if (!_IsActive)
return;
_IsActive = false;
_cancellationTokenSource.Cancel();
_workerQueue.CompleteAdding();
// Wait for the consumer's thread to finish.
for (int i = 0; i < _workers.Length; ++i)
{
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[i].Join(4000))
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
catch (Exception ex)
{
OnLogged(ex.Message + ex.StackTrace);
}
});
}
// Release any OS resources.
}
Anyone from microsoft got an idea? should i sleep after the cancelation and before calling the CompleteAdding?
Look at this piece of the code:
for (int i = 0; i < _workers.Length; ++i)
{
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[i].Join(4000)) << == Here
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
In _workers[i].Join(4000), the value of i is not what you think it is. Try again with:
for (int i = 0; i < _workers.Length; ++i)
{
int j = i; // copy
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[j].Join(4000)) // j
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
In your version, the variable 'i' is captured and all Tasks use the same var. All but the first few will see i == _workers.Length because they are executed after the for-loop is completed.
It is a classic lambda + captured var problem.

Categories

Resources