perform Action<> in main thread - c#

I'm writing an app, that performs very long requests at background. After each request I need to send result to main form.
So, here is a code:
Form1.cs
private async void StartButton_Click(object sender, EventArgs e)
{
await Logic.GenerateStackAsync(stackSettings, delegate(FullOrder transaction)
{
lastOrderId.Text = transaction.OrderId;
}
);
MessageBox.Show("Completed!");
}
Logic.cs:
public static bool GenerateStack(StackSettings stackSettings, Action<FullOrder> onOrderCreated = null)
{
for(var i = 0; i < 10; i++)
{
// long, long request, replaced with:
System.Threading.Thread.Sleep(10000);
if (onOrderCreated != null)
{
onOrderCreated.Invoke(order);
// tried to change it with onOrderCreated(order), no results.
}
}
return true;
}
public static Task<bool> GenerateStackAsync(StackSettings stackSettings, Action<FullOrder> onOrderCreated)
{
return TaskEx.Run(() => GenerateStack(stackSettings, onOrderCreated));
}
It throws an exception: "Control 'lastOrderId' accessed from a thread other than the thread it was created on.", which can be fixed by adding CheckForIllegalCrossThreadCalls = false;, but I think that this is a bad experience. How make it right? Thank you in advance.
P.S. Sorry for bad English.

First, do not expose (fake-)asynchronous wrappers for your synchronous methods.
Next, if you want to report progress updates, then use the progress update classes provided in .NET for that purpose.
public static bool GenerateStack(StackSettings stackSettings, IProgress<FullOrder> progress = null)
{
for(var i = 0; i < 10; i++)
{
// long, long request, replaced with:
System.Threading.Thread.Sleep(10000);
if (progress != null)
{
progress.Report(order);
}
}
return true;
}
Then, you can call it as such:
private async void StartButton_Click(object sender, EventArgs e)
{
var progress = new Progress<FullOrder>(transaction =>
{
lastOrderId.Text = transaction.OrderId;
});
await Task.Run(() => Logic.GenerateStack(stackSettings, progress));
MessageBox.Show("Completed!");
}

I would say that you need to use Control.Invoke to solve that problem:
See http://msdn.microsoft.com/library/system.windows.forms.control.invoke(v=vs.110).aspx

when you use async\await u actually starting new thread you do you stuff there and you want the result to show in the main thread the UIThread thats why you need to use the Control.Invoke

Related

How to avoid repeated while loops with C# event handler?

In a Windows Forms application, i have a ComboBox element and need to run some code pieces periodically when ComboBox element changes.
The problem with code is, when ComboBox Text changes A to B or vice versa ,event handler triggers and previous while(true) loops are still running.
When event handler is triggered, needs to run one infinite loop.
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
string status = comboBox.SelectedItem.ToString();
Task.Run(async () =>
{
while (true)
{
// Running on ui thread
datagrid.BeginInvoke((MethodInvoker)delegate
{
status = comboBox.SelectedItem.ToString();
});
if (status == "A")
{
Work();
}
else if (status == "B")
{
anotherWork();
}
else if (status == "C")
{
someWork();
break;
}
await Task.Delay(3000);
}
});
}
Tried with ManualResetEvent, CancellationTokenSource, and bool check but none of them solved the problem.
What is the best practice to prevent repeated while loops?
Edit: Implemented timer already but wanted to while loop trick. I was doing bool check in a wrong way, Owen s answer is accepted.
Add a bool to check whether or not the loop is running:
private bool _loopRunning;
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if(_loopRunning)
{
return;
}
_loopRunning = true;
string status = comboBox.SelectedItem.ToString();
Task.Run(async () =>
{
// stuff
_loopRunning = false;
});
}

Event after BackgroundWorker thread is aborted

Perhaps I am over thinking this but I have a number of threads doing a rather lengthy process and I want to be able to cleanly abort them if necessary. This is primarily because I don't want to start new threads until the old ones are finished. Is there a way to tell via Event or other method that a thread is fully aborted? Or should I not worry about this? Currently I have code in my abort method as follows:
private void AbortProcessing()
{
if (!Processing) return;
StopFlag = true;
for (int x = 0; x < MaxThreads; x++)
{
try
{
BW[x].CancelAsync();
}
catch { }
//Processing = false;
}
bool aborted = false;
while (!aborted)
{
aborted = true;
for (int x = 0; x < MaxThreads; x++)
{
if (BW[x].IsBusy) aborted = false;
}
Thread.Sleep(100);
}
}
The IsBusy is always true, forever, after signalling abort so this doesn't work. Any suggestions?
try this:Simple Sample for this
Thread o;
private void button1_Click(object sender, EventArgs e)
{
ThreadStart starter = ThreaadFunction;
starter += () => {
//this is this event you want to do any thing after a threaad finished the job
};
o = new Thread(starter) {IsBackground =true };
o.Start();
}
public void ThreaadFunction()
{
Thread.Sleep(1000);
// what you do in thread
}
}

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);
}
}
}
}

why do other events cannot be fired while "for loop" is in process?

I have a for loop and when the loop is being processed, I cant access any other function or event like clicking button it doesn't work till the for loop ends. Is there any way to overcome this Issue and hope I can get answer soon.
for (int i = 0; i < sizes - 2; i++)
{
if (pictureBox1.Image != null)
{
trackBar1.Value = trackBar1.Value + 1;
DisplayImage(_image);
}
}
Thanks in advance.
hi if you using framework 4.5
you can to the next :
Task.Run(() =>
{
for (int i = 0; i < sizes - 2; i++)
{
if (pictureBox1.Image != null)
{
trackBar1.Value = trackBar1.Value + 1;
DisplayImage(_image);
}
}
});
if not you can try this using thread :
Thread thread = new Thread(NewMethod);
thread.Start();
private void NewMethod()
{
for (int i = 0; i < sizes - 2; i++)
{
if (pictureBox1.Image != null)
{
trackBar1.Value = trackBar1.Value + 1;
DisplayImage(_image);
}
}
}
you can upgrade but you need to do it with delegate try this if you have cross thread operation error when update ui :
create delegate void function
delegate void Function();
then in your for make this :
Invoke(new Function(delegate()
{
label.text = "some text" ;
}));
This example shows how to create a new thread in .NET Framework. First, create a new ThreadStart delegate. The delegate points to a method that will be executed by the new thread. Pass this delegate as a parameter when creating a new Thread instance. Finally, call the Thread.Start method to run your method (in this case WorkThreadFunction) on background.
using System.Threading;
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Start();
The WorkThreadFunction could be defined as follows.
public void WorkThreadFunction()
{
try
{
// do any background work
}
catch (Exception ex)
{
// log errors
}
}

How to do progress reporting using Async/Await

suppose i have a list of files which i have to copy to web server using ftp related classes in c# project. here i want to use Async/Await feature and also want to show multiple progress bar for multiple file uploading at same time. each progress bar indicate each file upload status. so guide me how can i do this.
when we work with background worker to do this kind of job then it is very easy because background worker has progress change event. so how to handle this kind of situation with Async/Await. if possible guide me with sample code. thanks
Example code with progress from the article
public async Task<int> UploadPicturesAsync(List<Image> imageList,
IProgress<int> progress)
{
int totalCount = imageList.Count;
int processCount = await Task.Run<int>(() =>
{
int tempCount = 0;
foreach (var image in imageList)
{
//await the processing and uploading logic here
int processed = await UploadAndProcessAsync(image);
if (progress != null)
{
progress.Report((tempCount * 100 / totalCount));
}
tempCount++;
}
return tempCount;
});
return processCount;
}
private async void Start_Button_Click(object sender, RoutedEventArgs e)
{
int uploads=await UploadPicturesAsync(GenerateTestImages(),
new Progress<int>(percent => progressBar1.Value = percent));
}
If you want to report on each file independently you will have different base type for IProgress:
public Task UploadPicturesAsync(List<Image> imageList,
IProgress<int[]> progress)
{
int totalCount = imageList.Count;
var progressCount = Enumerable.Repeat(0, totalCount).ToArray();
return Task.WhenAll( imageList.map( (image, index) =>
UploadAndProcessAsync(image, (percent) => {
progressCount[index] = percent;
progress?.Report(progressCount);
});
));
}
private async void Start_Button_Click(object sender, RoutedEventArgs e)
{
int uploads=await UploadPicturesAsync(GenerateTestImages(),
new Progress<int[]>(percents => ... do something ...));
}

Categories

Resources