How to wait two BackgrandWorker's RunWorkerCompleted event finished? - c#

That's the code:
private void button1_Click(object sender, EventArgs e)
{
ParaClass pcs = new ParaClass();
pcs.strPath = textBox1.Text;
pcs.sendedGrid = ugSrc;
this.backgroundWorker1.RunWorkerAsync(pcs);
ParaClass pcsB = new ParaClass();
pcsB.strPath = textBox2.Text;
pcsB.sendedGrid = ultraGrid2;
this.backgroundWorker2.RunWorkerAsync(pcsB);
doSomething();
}
and in both backgrandworker1 & backgrandworker2 ' complet event ,i write code like this:
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk1();
}
private void backgroundWorker2_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk2();
}
now the problem is : the function doSomething() in button1's click event must wait both backgrandworker's complete event finish.
if i change doSomething() to
private void backgroundWorker2_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk2();
doSomething();
}
then,because there are two thread,i don't know which thread will finish first,so what is the solution

Create 2 flags which represent complete state of 2 BackgroundWorker.
Turn each flag on in the RunWorkerCompleted event, then call doSomething() method.
In doSomething method, check if both flags is on, then continue to do, otherwise, return.

Create 2 AutoResetEvents, set them when each Background worker finishes and wait for them all in the main method with a WaitHandle.
WaitHandle[] handles = new WaitHandle[] { new AutoResetEvent(false), new AutoResetEvent(false)};
private void button1_Click(object sender, EventArgs e)
{
ParaClass pcs = new ParaClass();
pcs.strPath = textBox1.Text;
pcs.sendedGrid = ugSrc;
this.backgroundWorker1.RunWorkerAsync(pcs);
ParaClass pcsB = new ParaClass();
pcsB.strPath = textBox2.Text;
pcsB.sendedGrid = ultraGrid2;
this.backgroundWorker2.RunWorkerAsync(pcsB);
WaitHandle.WaitAll(this.handles);
doSomething();
}
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk1();
((AutoResetEvent)this.handles[0]).Set();
}
private void backgroundWorker2_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk2();
((AutoResetEvent)this.handles[1]).Set();
}

Related

Invoke control - copy file blocks another thread

I have a problem with backgroundworker. I wrote program to copying files from local disc to network disk and I want to shows progressbar spinning in circles (not shows progress) when process is running.When I set backgroundworker into empty button everything works fine, but when I set backgroundworker into button which have function to coping files progressbar shows when copying process is finished. Why and how can I solved that? Below code.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
circularProgressBar1.BeginInvoke(new MethodInvoker(sd));
}
public void sd()
{
circularProgressBar1.Visible = true;
circularProgressBar1.Enabled = true;
circularProgressBar1.Style = ProgressBarStyle.Marquee;
}
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
circularProgressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
MessageBox.Show("End");
}
private void button1_Click_1(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void copy_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
List<string> pathList = new List<string>();
// for example add 1000 path
pathList.add("C:\test\2.jpg");
pathList.add("C:\test\3.jpg");
foreach(string in in pathList)
{
File.Copy(in,"D:\test2",true);
}
}
You need to put the File.Copy in backgroundWorker1_DoWork - this is where the background work is done.
You may activate the circularProgressBar before the call to backgroundWorker1.RunWorkerAsync and disable it in backgroundWorker1_RunWorkerCompleted.

how to return from the background worker to web UI in asp.net

background worker in asp.net does not write the content to the web UI when background worker completed. please tell me the reasons why. and how to recover.???
static BackgroundWorker bwProcess;
[WebMethod()]
public static int GetProgress()
{
return Percentage;
}
background worker starts when click event happen
protected void btnClick_Click(object sender, EventArgs e)
{
bwProcess = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
bwProcess.DoWork += new DoWorkEventHandler(bwProcess_DoWork);
bwProcess.ProgressChanged += new ProgressChangedEventHandler(bwProcess_ProgressChanged);
bwProcess.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwProcess_RunWorkerCompleted);
bwProcess.RunWorkerAsync("AsyncWorker");
}
do work event for the backgroundworker
void bwProcess_DoWork(object sender, DoWorkEventArgs e)
{
bwProcess.ReportProgress(1);
for (int i = 0; i <= 100; i++)
{
if (bwProcess.CancellationPending)
{
e.Cancel = true;
return;
}
bwProcess.ReportProgress(i);
Thread.Sleep(20);
}
e.Result = "100 %";
}
this part is not working. it run nut there is no response
void bwProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Response.WriteFile("D:\\Samples.xlsx");
}
You've passed something to e.Result in DoWork, but you don't anything with it in RunWorkerCompleted.
void bwProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var text = e.Result as string;
Response.Write(text);
Response.WriteFile("D:\\Samples.xlsx");
}

Running task in another thread and reporting to main thread afterwards [duplicate]

This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 8 years ago.
I am using UI with multiple buttons and I need to start background task on button click and report back to main thread + update UI when the task is finished.
This is currently part of my code:
private void button1Click(object sender, EventArgs e)
{
button1.Text = "Starting";
button1.Enabled = false;
button1Worker.RunWorkerAsync();
}
private void button1worker_DoWork(object sender, DoWorkEventArgs e)
{
toolStarter.startTool("button1");
}
private void button1worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Text = "Autoruns";
button1.Enabled = true;
}
This solution works as intended / expected, however the same code is repeated for 15 different buttons and that seem to be wrong to me.
Can you recommend some other way to do the same thing?
I did try ThreadPool queue but did not manage to update UI after task finished.
You can do this via async/await instead, which simplifies the code and eliminates the need for the background worker(s):
private async void button1Click(object sender, EventArgs e)
{
button1.Text = "Starting";
button1.Enabled = false;
await Task.Run(() => toolStarter.startTool("button1"));
button1.Text = "Autoruns";
button1.Enabled = true;
}
If your buttons are all doing the same operation, with only the string changing, you can refactor this out to support that:
private async Task ExecuteButton(Button button, string toolName)
{
button.Text = "Starting";
button.Enabled = false;
await Task.Run(() => toolStarter.startTool(toolName));
button.Text = "Autoruns";
button.Enabled = true;
}
private async void button1Click(object sender, EventArgs e)
{
await ExecuteButton(button1, "button1");
// Do any other specific stuff for after here
}
// Use for other buttons as needed
private async void button2Click(object sender, EventArgs e)
{
await ExecuteButton(button2, "button2");
}
// This is your helper class that actually does the work
public class ToolStarter
{
public void startTool(string name)
{
// Put your switch statement here to decide what work to do
}
}
public class ButtonWorker
{
private Button button;
private ToolStarter toolStarter;
public ButtonWorker(Button button, ToolStarter toolStarter)
{
this.button = button;
this.toolStarter = toolStarter;
this.button.Enabled = false;
this.button.Text = "Starting";
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
}
private void DoWork(object state)
{
this.toolStarter.startTool(button.Name);
// Pass control back to the UI thread so you can update your
// button text and enabled/status
this.button.Invoke(new Action(() =>
{
this.button.Text = "Autoruns";
this.button.Enabled = true;
}));
}
}
// This is just showing you how to use the ButtonWorker
public class ButtonWorkerTest
{
public ButtonWorkerTest()
{
var btn1 = new Button { Name = "button1" };
var btn2 = new Button { Name = "button2" };
var toolstarter = new ToolStarter(); // This is your class
var worker1 = new ButtonWorker(btn1, toolstarter);
var worker2 = new ButtonWorker(btn2, toolstarter);
}
}

C# Get Textbox value in backgroundworker dowork event

In my windows forms application I have a textbox and backgroundworker component. In dowork event of the backgroundworker I am trying to access value of the textbox. How can i do that? I'm getting following exception in dowork event handler code when I try to access value of the textbox:
Cross-thread operation not valid: Control 'txtFolderName' accessed from a thread other than the thread it was created on`
You can only access textbox / form controls in GUI thread, you can do so like that.
if(txtFolderName.InvokeRequired)
{
txtFolderName.Invoke(new MethodInvoker(delegate { name = txtFolderName.text; }));
}
try this
txtFolderName.Invoke((MethodInvoker)delegate
{
string strFolderName = txtFolderName.Text;
});
You need to use MethodInvoker. Like:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
MethodInvoker mi = delegate { txtFolderName.Text = "New Text"; };
if(this.InvokeRequired)
this.Invoke(mi);
};
You will have to invoke your TextBox on the main thread.
tb.Invoke((MethodInvoker) delegate
{
tb.Text = "Update your text";
});
Try This:
void DoWork(...)
{
YourMethod();
}
void YourMethod()
{
if(yourControl.InvokeRequired)
yourControl.Invoke((Action)(() => YourMethod()));
else
{
//Access controls
}
}
Hope This Help.
this is the another 2 methods i use.
//save the text value of txtFolderName into a local variable before run the backgroundworker.
string strFolderName;
private void btnExe_Click(object sender, EventArgs e)
{
strFolderName = txtFolderName.text;
backgroundworker.RunWorkerAsync();
}
private void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
backgroundworkerMethod(strFolderName);//get the value from strFolderName
...
}
----------------------------------------------------
private void btnExe_Click(object sender, EventArgs e)
{
backgroundworker.RunWorkerAsync(txtFolderName.text);//pass the value into backgroundworker as parameter/argument
}
private void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
backgroundworkerMethod(e.Argument.ToString());//get the value from event argument
...
}

why is this backgroundworker code causing this error: Parameter count mismatch

what is wrong with this code below? The conn_PageDeleted is coming from a background thread and i am trying to update a label every time i get a call back. I get an error stating
Parameter count mismatch.
Here is the code:
private void cmdDeletePage_Click(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblDeleteStatus.Text = "";
MessageBox.Show("Complete");
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Connecter conn = new Connecter("a", "m");
conn.PageDeleted += new Connecter.PageDeletedHandler(conn_PageDeleted);
bool success = conn.DeletePage(txtPageToDelete.Text, chkRecursive.Checked);
}
public delegate void UpdateLabelHandler(object sender, string name);
void conn_PageDeleted(object sender, string name)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new UpdateLabelHandler(UpdateMe));
}
else
{
lblDeleteStatus.Text = name;
}
}
private void UpdateMe(object sender_, string name_)
{
lblDeleteStatus.Text = name_;
}
You should pass the parameters to the UpdateMe method, try this:
void conn_PageDeleted(object sender, string name)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new UpdateLabelHandler(UpdateMe), new object[] {sender, name}); //<-- the update goes here
}
else
{
lblDeleteStatus.Text = name;
}
}
Your delegate has to match the signature of the event handler, something like this:
public delegate void UpdateLabelHandler(object sender, string strArgs);
Edit: Since you have edited the code to include this ... I will amend this accordingly....
Looking at your edited code, I have to question this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Connecter conn = new Connecter("a", "m");
conn.PageDeleted += new Connecter.PageDeletedHandler(conn_PageDeleted);
bool success = conn.DeletePage(txtPageToDelete.Text, chkRecursive.Checked);
}
You are wiring up a 'PageDeleted' event handler....and call 'DeletePage' method after it, I presume that in turn invokes the event handler 'conn_PageDeleted' within the 'DoWork' body, it goes out of scope when the 'BackgroundWorker' thread is finished...and since 'conn' is in local scope of the 'worker_DoWork' method, that gets destroyed, and somehow your event handler gets messed up! Can you confirm this?
Hope this helps,
Best regards,
Tom.

Categories

Resources