In the code below, is there a way to instead of always subscribing the updateWorker_DoWork method, pass it a method like this
public void GetUpdates(SomeObject blah)
{
//...
updateWorker.DoWork += new DoWorkEventHandler(blah);
//...
}
public void GetUpdates()
{
//Set up worker
updateWorker.WorkerReportsProgress = true;
updateWorker.WorkerSupportsCancellation = true;
updateWorker.DoWork += new DoWorkEventHandler(updateWorker_DoWork);
updateWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(updateWorker_RunWorkerCompleted);
updateWorker.ProgressChanged +=
new ProgressChangedEventHandler(updateWorker_ProgressChanged);
//Run worker
_canCancelWorker = true;
updateWorker.RunWorkerAsync();
//Initial Progress zero percent event
_thes.UpdateProgress(0);
}
For your RunWorkerAsync() you can pass any argument you like. You can just put a Func() or Action() into it and in your DoWork() you just cast the object back to this specific type and call it.
Examples are here and here.
private void InitializeBackgroundWorker()
{
_Worker = new BackgroundWorker();
// On a call cast the e.Argument to a Func<TResult> and call it...
// Take the result from it and put it into e.Result
_Worker.DoWork += (sender, e) => e.Result = ((Func<string>)e.Argument)();
// Take the e.Result and print it out
// (cause we will always call a Func<string> the e.Result must always be a string)
_Worker.RunWorkerCompleted += (sender, e) =>
{
Debug.Print((string)e.Result);
};
}
private void StartTheWorker()
{
int someValue = 42;
//Take a method with a parameter and put it into another func with no parameter
//This is called currying or binding
StartTheWorker(new Func<string>(() => DoSomething(someValue)));
while(_Worker.IsBusy)
Thread.Sleep(1);
//If your function exactly matches, just put it into the argument.
StartTheWorker(AnotherTask);
}
private void StartTheWorker(Func<string> func)
{
_Worker.RunWorkerAsync(func);
}
private string DoSomething(int value)
{
return value.ToString("x");
}
private string AnotherTask()
{
return "Hello World";
}
If I didn't misunderstand you, you need lambda expressions to construct anonymous method.
updateWorker.DoWork += (sender,e)=>
{
//bla
}
Now you needn't always to write a method and pass it to new DoWorkEventHandler(myMethod)
Worked it out, was way simpler than I was thinking. Just had to make a delegate for the method called on DoWork. Probably should have phrased my original question better.
public delegate void DoWorkDelegate(object sender,DoWorkEventArgs e);
public void GetUpdates()
{
StartWorker(new DoWorkDelegate(updateWorker_DoWork));
}
public void StartWorker(DoWorkDelegate task)
{
//Set up worker
updateWorker.WorkerReportsProgress = true;
updateWorker.WorkerSupportsCancellation = true;
updateWorker.DoWork += new DoWorkEventHandler(task);
updateWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(updateWorker_RunWorkerCompleted);
updateWorker.ProgressChanged +=
new ProgressChangedEventHandler(updateWorker_ProgressChanged);
//Run worker
_canCancelWorker = true;
updateWorker.RunWorkerAsync();
//Initial Progress zero percent event
_thes.UpdateProgress(0);
}
private void updateWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = GetUpdatesTask(worker, e);
}
Related
I'm trying to write a method that attaches adds itself to an event, and then removes itself after it was invoked.
Those are my failed attempts:
https://imgur.com/a/PvrPUYY
public Action myEvent = () => {};
void Bar(){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () =>
{
Debug.Log("Attempt 0");
myEvent -= invokeThenUnsubscribe;
};
myEvent += invokeThenUnsubscribe;
SubscribeOnce(myEvent, () => Debug.Log("Attempt 1"));
myEvent.SubscribeOnce(() => Debug.Log("Attempt 2"));
myEvent.Invoke();
myEvent.Invoke();
}
public Action SubscribeOnce( Action a, Action actionToSubscribe ) {
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () =>
{
actionToSubscribe.Invoke();
a -= invokeThenUnsubscribe;
};
a += invokeThenUnsubscribe;
return a;
}
public static class ActionsEx {
public static void SubscribeOnce(this Action a, Action actionToSubscribe){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () => {
actionToSubscribe.Invoke();
a -= invokeThenUnsubscribe;
};
a += invokeThenUnsubscribe;
}
}
I realize it's happening because, after using +=, I lose the reference to the new event.
Does anyone have any idea how I can achieve the effect I'm looking for?
Thanks in advance
When you call SubscribeOnce, you aren't passing a reference to "the event" (technically a field, but: semantics); instead, you're reading the current value of the event (field), and passing that to the method. At that point, it the parameter value is completely divorced from the event (field), and no change to a (now a captured parameter) will have any effect on the original event (field). You're only "unsubscribing" a local delegate that has nothing to do with the event (field).
To do what you'd want, you'd need to pass the originating object in as the parameter, i.e. something like:
public static void SubscribeOnce(this SomeType obj, Action actionToSubscribe){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () => {
actionToSubscribe.Invoke();
obj.TheEvent -= invokeThenUnsubscribe;
};
obj.TheEvent += invokeThenUnsubscribe;
}
}
where SomeType (which could be an interface) defines an event Action TheEvent;, with usage this.SubscribeOnce(() => Debug.Log("Attempt 2")); (or whatever object you want to use)
I am not crazy about the fact that you are mixing actions with events. Instead, I would use a more object oriented approach and separate the two concepts by creating an interface to represent the subscribable objects that expose some event and then use an extension method to allow all actions to subscribe once to these objects:
//Delegate to handle events
public delegate void SomeEventEventHandler(ISubscribable sender, EventArgs args);
//Interface that describes an object you can subscribe to
public interface ISubscribable
{
event SomeEventEventHandler MyEvent;
}
//Implementation of a subscribable object (base class)
public class SomeObject : ISubscribable
{
private SomeEventEventHandler _handler;
public event SomeEventEventHandler MyEvent
{
add { _handler += value; }
remove { _handler -= value; }
}
public void RaiseEvent()
{
_handler?.Invoke(this, new EventArgs());
}
}
//Extension method for actions
public static class ActionExtensions
{
public static void SubscribeOnce(this Action action, ISubscribable subscribable)
{
SomeEventEventHandler handler = null;
handler = new SomeEventEventHandler((ISubscribable subscribable, EventArgs args) =>
{
action();
subscribable.MyEvent -= handler;
});
subscribable.MyEvent += handler;
}
}
//Usage:
var obj = new SomeObject();
obj.MyEvent += (ISubscribable sender, EventArgs args) =>
{
Console.WriteLine("Attempt 0 (always fires).");
};
Action invokeThenUnsubscribe = () => { Console.WriteLine("Attempt 1"); };
invokeThenUnsubscribe.SubscribeOnce(obj);
Action invokeThenUnsubscribe2 = () => { Console.WriteLine("Attempt 2"); };
invokeThenUnsubscribe2.SubscribeOnce(obj);
obj.RaiseEvent();
obj.RaiseEvent();
obj.RaiseEvent();
Output:
Attempt 0 (always fires).
Attempt 1
Attempt 2
Attempt 0 (always fires).
Attempt 0 (always fires).
Alternatively, you can skip the action extension all together and provide this functionality as a method of the class:
//In class SomeObject
public void SubscribeOnce(Action action)
{
SomeEventEventHandler handler = null;
handler = new SomeEventEventHandler((ISubscribable subscribable, EventArgs args) =>
{
action();
subscribable.MyEvent -= handler;
});
this.MyEvent += handler;
}
//Usage:
var obj2 = new SomeObject();
obj2.MyEvent += (ISubscribable sender, EventArgs args) =>
{
Console.WriteLine("Attempt 0 (always fires).");
};
obj2.SubscribeOnce(() => { Console.WriteLine("Attempt 1"); });
obj2.SubscribeOnce(() => { Console.WriteLine("Attempt 2"); });
obj2.RaiseEvent();
obj2.RaiseEvent();
obj2.RaiseEvent();
This produces the same output.
I'm trying to update a button control from a child thread.
I get some problems with passing parameters to the new thread.
I get the following message:
No overload for 'UpdateText' matches delegate 'System.Threading.ParameterizedThreadStart' (CS0123)
As far as I understand ParameterizedThreadStart takes and type "object" argument. How can I cast the object "button1" to Button in my UpdateText method?
public delegate void MyDelegate(Control ctrl);
void Button1Click(object sender, EventArgs e)
{
Thread thr =new Thread(new ParameterizedThreadStart(UpdateText));
thr.Start(button1);
}
public static void UpdateText(Control control_button)
{
if (control_button.InvokeRequired)
{
MyDelegate md = new MyDelegate(UpdateText);
control_button.Invoke(md, control_button);
}
else
{
control_button.Text = "Updated";
}
}
change UpdateText argument to Object:
public static void UpdateText(Object o)
{
Control control_button = (Control) o;
// ... the rest of your code ...
Check this reference on ParametrizedThreadStart:
Also at this line, i don't really understand what you're trying:
MyDelegate md = new MyDelegate(UpdateText);
control_button.Invoke(md, control_button);
Did you mean to:
control_button.Invoke( () => {
control_button.Text = "Updated";
});
or
control_button.Invoke(MyDelegate, control_button);
in my WPF - C# application, I have a time consuming function, which I execute with a BackgroundWorker. The job of this function is to add given data from a file into a database. Now and then, I need some user feedback, for example the data is already in the store and I want to ask the user, whether he wants to merge the data or create a new object or skip the data completely. Much like the dialog windows shows, if I try to copy a file to a location, where a file with the same name already exists.
The problem is, that I cannot call a GUI-window from a non GUI-thread. How could I implement this behavior?
Thanks in advance,
Frank
You could work with EventWaitHandle ou AutoResetEvent, then whenever you want to prompt the user, you could the signal UI, and then wait for the responde. The information about the file could be stored on a variable.
If possible... my suggestion is to architect your long running task into atomic operations. Then you can create a queue of items accessible by both your background thread and UI thread.
public class WorkItem<T>
{
public T Data { get; set; }
public Func<bool> Validate { get; set; }
public Func<T, bool> Action { get; set; }
}
You can use something like this class. It uses a queue to manage the execution of your work items, and an observable collection to signal the UI:
public class TaskRunner<T>
{
private readonly Queue<WorkItem<T>> _queue;
public ObservableCollection<WorkItem<T>> NeedsAttention { get; private set; }
public bool WorkRemaining
{
get { return NeedsAttention.Count > 0 && _queue.Count > 0; }
}
public TaskRunner(IEnumerable<WorkItem<T>> items)
{
_queue = new Queue<WorkItem<T>>(items);
NeedsAttention = new ObservableCollection<WorkItem<T>>();
}
public event EventHandler WorkCompleted;
public void LongRunningTask()
{
while (WorkRemaining)
{
if (_queue.Any())
{
var workItem = _queue.Dequeue();
if (workItem.Validate())
{
workItem.Action(workItem.Data);
}
else
{
NeedsAttention.Add(workItem);
}
}
else
{
Thread.Sleep(500); // check if the queue has items every 500ms
}
}
var completedEvent = WorkCompleted;
if (completedEvent != null)
{
completedEvent(this, EventArgs.Empty);
}
}
public void Queue(WorkItem<T> item)
{
// TODO remove the item from the NeedsAttention collection
_queue.Enqueue(item);
}
}
Your UI codebehind could look something like
public class TaskRunnerPage : Page
{
private TaskRunner<XElement> _taskrunner;
public void DoWork()
{
var work = Enumerable.Empty<WorkItem<XElement>>(); // TODO create your workItems
_taskrunner = new TaskRunner<XElement>(work);
_taskrunner.NeedsAttention.CollectionChanged += OnItemNeedsAttention;
Task.Run(() => _taskrunner.LongRunningTask()); // run this on a non-UI thread
}
private void OnItemNeedsAttention(object sender, NotifyCollectionChangedEventArgs e)
{
// e.NewItems contains items that need attention.
foreach (var item in e.NewItems)
{
var workItem = (WorkItem<XElement>) item;
// do something with workItem
PromptUser();
}
}
/// <summary>
/// TODO Use this callback from your UI
/// </summary>
private void OnUserAction()
{
// TODO create a new workItem with your changed parameters
var workItem = new WorkItem<XElement>();
_taskrunner.Queue(workItem);
}
}
This code is untested! But the basic principle should work for you.
Specifically to your case
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000);
var a = Test1("a");
Thread.Sleep(1000);
var b = (string)Invoke(new Func<string>(() => Test2("b")));
MessageBox.Show(a + b);
}
private string Test1(string text)
{
if (this.InvokeRequired)
return (string)this.Invoke(new Func<string>(() => Test1(text)));
else
{
MessageBox.Show(text);
return "test1";
}
}
private string Test2(string text)
{
MessageBox.Show(text);
return "test2";
}
Test2 is a normal method which you have to invoke from background worker. Test1 can be called directly and uses safe pattern to invoke itself.
MessageBox.Show is similar to yourForm.ShowDialog (both are modal), you pass parameters to it (text) and you return value (can be a value of property of yourForm which is set when form is closed). I am using string, but it can be any data type obviously.
From the input of the answers here, I came to the following solution:
(Mis)Using the ReportProgress-method of the Backgroundworker in Combination with a EventWaitHandle. If I want to interact with the user, I call the ReportProgress-method and setting the background process on wait. In the Handler for the ReportProgress event I do the interaction and when finished, I release the EventWaitHandle.
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
}
// Starting the time consuming operation
private void Button_Click(object sender, RoutedEventArgs e)
{
bgw.RunWorkerAsync();
}
// using the ProgressChanged-Handler to execute the user interaction
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UserStateData usd = e.UserState as UserStateData;
// UserStateData.Message is used to see **who** called the method
if (usd.Message == "X")
{
// do the user interaction here
UserInteraction wnd = new UserInteraction();
wnd.ShowDialog();
// A global variable to carry the information and the EventWaitHandle
Controller.instance.TWS.Message = wnd.TextBox_Message.Text;
Controller.instance.TWS.Background.Set();
}
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
}
// our time consuming operation
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(2000);
// need 4 userinteraction: raise the ReportProgress event and Wait
bgw.ReportProgress(0, new UserStateData() { Message = "X", Data = "Test" });
Controller.instance.TWS.Background.WaitOne();
// The WaitHandle was released, the needed information should be written to global variable
string first = Controller.instance.TWS.Message.ToString();
// ... and again
Thread.Sleep(2000);
bgw.ReportProgress(0, new UserStateData() { Message = "X", Data = "Test" });
Controller.instance.TWS.Background.WaitOne();
e.Result = first + Controller.instance.TWS.Message;
}
I hope I did not overlooked some critical issues. I'm not so familar with multithreading - maybe there should be some lock(object) somewhere?
Let's say I want to sent an int parameter to a background worker, how can this be accomplished?
private void worker_DoWork(object sender, DoWorkEventArgs e) {
}
I know when this is worker.RunWorkerAsync();, I don't understand how to define in worker_DoWork that it should take an int parameter.
You start it like this:
int value = 123;
bgw1.RunWorkerAsync(argument: value); // the int will be boxed
and then
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
int value = (int) e.Argument; // the 'argument' parameter resurfaces here
...
// and to transport a result back to the main thread
double result = 0.1 * value;
e.Result = result;
}
// the Completed handler should follow this pattern
// for Error and (optionally) Cancellation handling
private void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
// check error, check cancel, then use result
if (e.Error != null)
{
// handle the error
}
else if (e.Cancelled)
{
// handle cancellation
}
else
{
double result = (double) e.Result;
// use it on the UI thread
}
// general cleanup code, runs when there was an error or not.
}
Even though this is an already answered question, I'd leave another option that IMO is a lot easier to read:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (obj, e) => WorkerDoWork(value, text);
worker.RunWorkerAsync();
And on the handler method:
private void WorkerDoWork(int value, string text) {
...
}
You can pass multiple arguments like this.
List<object> arguments = new List<object>();
arguments.Add("first"); //argument 1
arguments.Add(new Object()); //argument 2
// ...
arguments.Add(10); //argument n
backgroundWorker.RunWorkerAsync(arguments);
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> genericlist = e.Argument as List<object>;
//extract your multiple arguments from
//this list and cast them and use them.
}
You can use the DoWorkEventArgs.Argument property.
A full example (even using an int argument) can be found on Microsoft's site:
How to: Run an Operation in the Background
Check out the DoWorkEventArgs.Argument Property:
...
backgroundWorker1.RunWorkerAsync(yourInt);
...
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Extract the argument.
int arg = (int)e.Argument;
// Start the time-consuming operation.
e.Result = TimeConsumingOperation(bw, arg);
// If the operation was canceled by the user,
// set the DoWorkEventArgs.Cancel property to true.
if (bw.CancellationPending)
{
e.Cancel = true;
}
}
you can try this out if you want to pass more than one type of arguments, first add them all to an array of type Object and pass that object to RunWorkerAsync() here is an example :
some_Method(){
List<string> excludeList = new List<string>(); // list of strings
string newPath ="some path"; // normal string
Object[] args = {newPath,excludeList };
backgroundAnalyzer.RunWorkerAsync(args);
}
Now in the doWork method of background worker
backgroundAnalyzer_DoWork(object sender, DoWorkEventArgs e)
{
backgroundAnalyzer.ReportProgress(50);
Object[] arg = e.Argument as Object[];
string path= (string)arg[0];
List<string> lst = (List<string>) arg[1];
.......
// do something......
//.....
}
You need RunWorkerAsync(object) method and DoWorkEventArgs.Argument property.
worker.RunWorkerAsync(5);
private void worker_DoWork(object sender, DoWorkEventArgs e) {
int argument = (int)e.Argument; //5
}
You should always try to use a composite object with concrete types (using composite design pattern) rather than a list of object types. Who would remember what the heck each of those objects is? Think about maintenance of your code later on... Instead, try something like this:
Public (Class or Structure) MyPerson
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public int ZipCode { get; set; }
End Class
And then:
Dim person as new MyPerson With { .FirstName = “Joe”,
.LastName = "Smith”,
...
}
backgroundWorker1.RunWorkerAsync(person)
and then:
private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
{
MyPerson person = e.Argument as MyPerson
string firstname = person.FirstName;
string lastname = person.LastName;
int zipcode = person.ZipCode;
}
I'm using following code to call Method B after N seconds method A is called. If method A
is called again within the N seconds timeout, i have to reset the time counting back to N seconds.
I cannot reference System.Windows.Form in my project, so I cannot use System.Windows.Form.Timer.
The method B must be called in the same thread A is called.
private void InitTimer()
{
timer = new BackgroundWorker();
timer.WorkerSupportsCancellation = true;
timer.WorkerReportsProgress = true;
timer.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var st = DateTime.Now;
while (DateTime.Now.Subtract(st).TotalSeconds < 10)
{
if (timer.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
timer.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
MethodB();
}
else
{
timer.RunWorkerAsync();
}
};
}
public void MethodA()
{
if (timer.IsBusy)
timer.CancelAsync();
else
timer.RunWorkerAsync();
}
public void MethodB()
{
//do some stuff
}
Actually the code work, but i think it's a bit confounding. Do you know if there is a best practices to achieve the same result?
It's a shame you're stuck on .NET 2.0, because Rx extensions has a Throttle method that achieves this effect quite elegantly.
Sadly Rx requires at least .NET 3.5 SP1.
Oh well! You can always use a System.Threading.Timer to get this done instead. Synchronization can be provided by leveraging the current SynchronizationContext (this is what BackgroundWorker does).
Here's a sketch of a LaggedMethodPair class to illustrate this approach. The class takes three inputs in its constructor: an Action to be performed on-demand, another Action to serve as the callback that will be invoked when a given timeout has elapsed, and, of course, the timeout itself:
public sealed class LaggedMethodPair
{
private SynchronizationContext _context;
private Timer _timer;
private Action _primaryAction;
private Action _laggedCallback;
private int _millisecondsLag;
public LaggedMethodPair(Action primaryAction,
Action laggedCallback,
int millisecondsLag)
{
if (millisecondsLag < 0)
{
throw new ArgumentOutOfRangeException("Lag cannot be negative.");
}
// Do nothing by default.
_primaryAction = primaryAction ?? new Action(() => { });
// Do nothing by default.
_laggedCallback = laggedCallback ?? new Action(() => { });
_millisecondsLag = millisecondsLag;
_timer = new Timer(state => RunTimer());
}
public void Invoke()
{
// Technically there is a race condition here.
// It could be addressed, but in practice it will
// generally not matter as long as Invoke is always
// being called from the same SynchronizationContext.
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext()
);
}
_context = SynchronizationContext.Current;
ResetTimer();
_primaryAction();
}
void ResetTimer()
{
_timer.Change(_millisecondsLag, Timeout.Infinite);
}
void RunTimer()
{
_context.Post(state => _laggedCallback(), null);
}
}
I wrote a sample Windows Forms app to show this class in action. The form contains a LaggedMethodPair member with a timeout of 2000 ms. Its primaryAction adds an item to a list view. Its laggedCallback adds a highlighted item to the list view.
You can see that the code runs as expected.
I would encapsulate this functionality into a timer class with events that other classes can subscribe to (for example a timer.tick event).
I am trying to use AutoResetEvent, because it is capable to wait for a signal. I use it to have worker waited for the signal from A(), and if it has been too long B() will be called.
class Caller
{
AutoResetEvent ev = new AutoResetEvent(false);
public void A()
{
ev.Set();
// do your stuff
Console.Out.WriteLine("A---");
}
void B()
{
Console.Out.WriteLine("B---");
}
public void Start()
{
var checker = new BackgroundWorker();
checker.DoWork += new DoWorkEventHandler(checker_DoWork);
checker.RunWorkerAsync();
}
void checker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (!worker.CancellationPending)
{
bool called = ev.WaitOne(TimeSpan.FromSeconds(3));
if (!called) B();
}
}
}
I have tested my class roughly and it is working fine so far. Note that B will be called from worker thread, so you have to do the synchronization in B() if needed.