I am currently trying to make a regular function run as an anonymous BackgroundWorker's DoWork event. The issue I have is that the method is not running at all. The current code I have is as follows;-
public class Worker
{
BackgroundWorker worker;
public Worker(Func<bool> action)
{
worker = new BackgroundWorker();
worker.DoWork += (sender, e) => e.Result = action;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
this.action = action;
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("Thread completed : "+ e.Result.ToString());
}
public void DoWork()
{
Console.WriteLine("worker thread: working...");
worker.RunWorkerAsync();
//Wait for worker to complete
do { } while (worker.IsBusy);
}
}
The function is passed like this:-
Worker workerObject = new Worker(new Func<bool>(() => methodThatReturnsBool(param1, param2)));
Thread workerThread = new Thread(workerObject.DoWork);
workerThread.Start();
How is it possible to pass the method and have it run within the background worker?
From the looks of it, you are just assigning the action itself as a result, instead of calling it.
worker.DoWork += (sender, e) => e.Result = action();
Also the waiting loop might cause problems. At least put a
do {Thread.Yield();} while (worker.IsBusy);
in there
Or use a cleaner (no busy-waiting) approach:
public class Worker
{
private BackgroundWorker _worker;
private AutoResetEvent _event;
private Func<bool> _action;
public Worker(Func<bool> action)
{
_action = action;
_event = new AutoResetEvent(false);
_worker = new BackgroundWorker();
_worker.DoWork += (sender, e) =>
{
try
{
e.Result = _action();
}
finally
{
_event.Set();
}
};
_worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("Thread completed : "+ e.Result.ToString());
}
public void DoWork()
{
Console.WriteLine("worker thread: working...");
_worker.RunWorkerAsync();
_event.WaitOne();
}
}
Related
I have modified Background worker private AbortableBackgroundWorker _worker;
public class AbortableBackgroundWorker : BackgroundWorker
{
//Internal Thread
private Thread _workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (_workerThread != null)
{
_workerThread.Abort();
_workerThread = null;
}
}
}
And have method which init BgWorker
private void BusyLoader(Action doWorkAction)
{
if (_worker == null)
{
_worker = new AbortableBackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (sender, e) => _worker_DoWork(sender, e, doWorkAction);
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
}
if (!_worker.IsBusy)
_worker.RunWorkerAsync();
}
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
loadingPanel.StopSpin();
_worker.Abort();
_worker.Dispose();
}
private void _worker_DoWork(object sender, DoWorkEventArgs e, Action action)
{
loadingPanel.StartSpin();
this.Dispatcher.Invoke(action);
}
When I call method BusyLoader I want to pass there Action, which should be executed and at this time busy Indicator should be shown.
I have tried It. And it seems to work but only for first call of BusyLoader. Because _worker.DoWork has the same method, as I understand.
How can I manage to change _worker.DoWork method for every new call of BusyLoader ? Or it is bad approach to pass Action like that?
You said it helped so will post comment as an answer
_worker is not null on the second call so _worker_DoWork is not redefined. Try removing and adding.
I have created a Form for long-running Tasks, it's purpose is to loop a Progress Bar until the Thread completes. I display this Form as a Dialog so that my main application is Waiting until this Form returns.
Unfortunately, my RunWorkerCompleted is being called before the Thread actually finishes its task.
What is an effective method of Waiting for a Thread to Finish, WITHOUT Joining?
public partial class FormProcessing : Form
{
public ThreadStart StartTaskFunc { get; set; }
public FormProcessing()
{
InitializeComponent();
bgWorker.DoWork += bgWorker_DoWork;
bgWorker.ProgressChanged += bgWorker_ProgressChanged;
bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
Shown += (s, e) =>
{
bgWorker.RunWorkerAsync();
};
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Close();
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
var thread = new Thread(() => StartTaskFunc());
thread.Start();
int i = 0;
while (thread.IsAlive)
{
if (i == 26)
i = 0;
bgWorker.ReportProgress(i);
Task.Delay(200).Wait();
i++;
}
}
}
You are creating another "Thread" in "bgWorker_DoWork" method.
If you create another thread, the "bgWorker_DoWork" is completed as soon as the new thread is created.
I'm trying to make a helper function to make BackgroundWorkers.
Here is what I have so far.
using System.ComponentModel;
using System;
public class BackgroundThread {
BackgroundWorker worker;
public BackgroundThread(Delegate workerFunction, Delegate workerCallback) {
this.worker = new BackgroundWorker();
this.worker.DoWork += new DoWorkEventHandler(workerFunction);
this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerCallback);
}
public void Start(object argument) {
this.worker.RunWorkerAsync(argument);
}
}
Although I am getting this error.
Expression denotes a 'variable', where a 'type' or 'method group' was expected
It makes sense since normally you pass a reference to a function to the Handlers but I don't know how to do it in this context. Or is this just not possible. I don't know enough about C# delegates and such to know what to do.
Like this?
public class BackgroundThread
{
System.ComponentModel.BackgroundWorker worker;
public BackgroundThread(System.ComponentModel.DoWorkEventHandler workerFunction, System.ComponentModel.RunWorkerCompletedEventHandler workerCallback)
{
this.worker = new System.ComponentModel.BackgroundWorker();
this.worker.DoWork += workerFunction;
this.worker.RunWorkerCompleted += workerCallback;
}
public BackgroundThread(Action<object> anyWorkFunctionWithObjectArgument, Action<object> anyCallback)
{
this.worker = new System.ComponentModel.BackgroundWorker();
this.worker.DoWork += (sender, e) => { anyWorkFunctionWithObjectArgument.Invoke(e.Argument); };
this.worker.RunWorkerCompleted += (sender, e) => { anyCallback.Invoke(e.Result); };
}
public void Start(object argument)
{
this.worker.RunWorkerAsync(argument);
}
public static BackgroundThread GetDoNothingInstance()
{
return new BackgroundThread(
(sender, e) =>
{
// e is DoWorkEventArgs
},
(sender, e) =>
{
// e is RunWorkerCompletedEventArgs
});
}
public static BackgroundThread GetDoNothingInstance2()
{
Action<object> workfunction = delegate(object argument)
{
// Do nothing
};
Action<object> callback = delegate(object result)
{
// Do nothing
};
return new BackgroundThread(workfunction, callback);
}
}
Just saw your comment. This should allow you to just pass a "plain old function" without having to shape it like a handler:
class Program
{
protected static void plainOldWorkerFunction(object argument)
{
return;
}
protected static void plainOldCallbackFunction()
{
return;
}
static void Main(string[] args)
{
BackgroundThread bt = new BackgroundThread(plainOldWorkerFunction, plainOldCallbackFunction);
bt.Start(1234);
}
}
public class BackgroundThread
{
BackgroundWorker worker;
Action<object> workerAction;
Action callbackAction;
protected void doWork(object sender, DoWorkEventArgs e)
{
workerAction(e.Argument);
}
protected void callback(object sender, RunWorkerCompletedEventArgs e)
{
callbackAction();
}
public BackgroundThread(Action<object> workerFunction, Action workerCallback)
{
this.workerAction = workerFunction;
this.callbackAction = workerCallback;
this.worker = new BackgroundWorker();
this.worker.DoWork += doWork;
this.worker.RunWorkerCompleted += callback;
}
public void Start(object argument)
{
this.worker.RunWorkerAsync(argument);
}
}
Original answer:
Try this constructor instead:
public BackgroundThread(DoWorkEventHandler workerFunction, RunWorkerCompletedEventHandler workerCallback)
{
this.worker = new BackgroundWorker();
this.worker.DoWork += workerFunction;
this.worker.RunWorkerCompleted += workerCallback;
}
And just make sure your workerFunction and workerCallback have these parameters:
protected static void workerFunction (object sender, DoWorkEventArgs e)
{
return;
}
protected static void workerCallback (object sender, RunWorkerCompletedEventArgs e)
{
return;
}
I have main program
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Worker w1 = new Worker(1);
Worker w2 = new Worker(2);
Thread w1Thread = new Thread(new ThreadStart(w1.StartWorking));
Thread w2Thread = new Thread(new ThreadStart(w2.StartWorking));
w1Thread.Start();
w2Thread.Start();
Application.Run(new MainWindow());
if (w1Thread.IsAlive)
{
w1Thread.Abort();
}
if (w2Thread.IsAlive)
{
w2Thread.Abort();
}
}
}
and worker class:
class Worker
{
public int m_workerId;
public bool m_workerLifeBit;
public bool m_workerWork;
public Worker(int id)
{
m_workerId = id;
m_workerLifeBit = false;
}
public void StartWorking()
{
while (!m_workerWork)
{
m_workerLifeBit = false;
System.Threading.Thread.Sleep(5000);
m_workerLifeBit = true;
System.Threading.Thread.Sleep(5000);
}
}
}
I have checkBox on MainWindow form.
How to monitor state of Worker variable m_workerLifeBit and display its changes in MainWindow checkBox?
I have found this q&a How to update the GUI from another thread in C#? hovewer the answer does not show complete example, and I failed with using thread safe delegate.
I want some event mechanism that I fire in Worker.StartWorking and catch in slot in MainWindow form.
Here is a simple version using events:
class Worker
{
public event Action<bool> WorkerLifeBitChanged;
// ...
public void StartWorking()
{
// ...
m_workerLifeBit = false;
OnWorkerLifeBitChanged();
// ...
private void OnWorkerLifeBitChanged()
{
if (WorkerLifeBitChanged != null)
WorkerLifeBitChanged(m_workerLifeBit);
}
Then you wire up the event in Main:
//...
var mainWindow = new MainWindow();
w1.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w2.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w1Thread.Start();
w2Thread.Start();
Application.Run(mainWindow);
//...
And UpdateWorkerLifeBit implementation in MainWindow:
public void UpdateWorkerLifeBit(bool workerLifeBit)
{
if (this.checkBox.InvokeRequired)
{
this.Invoke(new Action(() => checkBox.Checked = workerLifeBit));
}
else
{
checkBox.Checked = workerLifeBit;
}
}
As mentioned in the comments, if this is a WinForms application then I'd recommend using a BackgroundWorker.
Kicking off the bg worker and subscribing to events:
BackgroundWorker worker = new BackgroundWorker();
// Subscribing to the worker method. Do all of your work here
worker.DoWork += worker_DoWork;
// Subscribing to the progress changed event where you'll want to update the UI
worker.ReportProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
// Subscribing to the worker completed event. Fires when the work is complete
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
// This line starts the worker
worker.RunWorkerAsync();
You would then have your methods defined as such:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Perform some work with the object you've passed in e.g.
MyObj foo = (MyObj)e.Argument;
foo.Name = "foobar";
// Notify UI
worker.ReportProgress(100, foo);
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update UI
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Worker has finished
}
One solution would be passing a reference of your Program class (or even a delegate in your program class, or a data reference in your worker class) to the Worker thread. You can call a function of your Program directly from the thread code then. You can also use signals, but for this small example my previous "solution" is acceptable.
I have sth like that. It's giving me error. I cut out all unneeded parts of code. It is giving me this error
The calling thread cannot access this object because a different thread owns it.
public partial class MainWindow : Window
{
BackgroundWorker worker;
Grafik MainGrafik;
double ProgressBar
{
set { this.progressBarMain.Value = value; }
}
public MainWindow()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
MainGrafik = new Grafik();
MainGrafik.ProgressUpdate +=
new Grafik.ProgressUpdateDelegate(MainGrafik_ProgressUpdate);
worker.RunWorkerAsync();
}
void MainGrafik_ProgressUpdate(double progress)
{
ProgressBar = progress;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
MainGrafik.Refresh();
Thread.Sleep(2000);
}
}
}
class Grafik
{
public delegate void ProgressUpdateDelegate(double progress,
DateTime currTime);
public event ProgressUpdateDelegate ProgressUpdate;
public void Refresh()
{
ProgressUpdate(5); // Just for testing
}
}
You can't update UI objects from another thread. They have to be updated in the UI thread. Try adding this code to the MainGrafik_ProgressUpdate(double progress)
void MainGragfik_ProgressUpdate(double progress)
{
if (InvokeRequired)
{
BeginInvoke((MethodIvoker)(() =>
{
MainGragfik_ProgressUpdate(progress);
}));
return;
}
ProgressBar = progress;
}
The thread firing the ProgressUpdate event is your BackgroundWorker. The ProgressUpdate event handlers are likely running on that thread, and not the UI thread.
in short call this on the form in the context of your other thread's execution:
void MainGrafik_ProgressUpdate(object sender, EventArgs e) {
Action<T> yourAction =>() yourAction;
if(yourForm.InvokeRequired)
yourForm.Invoke(yourAction);
else yourAction;
}
Or with MethodInvoker (blank delegate)
void MainGrafik_ProgressUpdate(object sender, EventArgs e) {
MethodInvoker invoker = delegate(object sender, EventArgs e) {
this.ProgressBar = whatever progress;
};
}