I have several timers with different intervals. Tasks can be long running and they may overlap, but at any time I must be sure only one task is active. I have the following code with a bool variable which I am setting in event handlers:
public partial class SomeService : ServiceBase
{
private volatile bool IsActive;
private System.Timers.Timer Timer1;
private System.Timers.Timer Timer2;
protected override void OnStart(string[] args)
{
Timer1 = new System.Timers.Timer();
Timer1.Interval = ConfigurationManager.ImportEveryMinute1 * 60 * 1000;
Timer1.Elapsed += new System.Timers.ElapsedEventHandler(Timer1_Elapsed);
Timer1.Start();
Timer2 = new System.Timers.Timer();
Timer2.Interval = ConfigurationManager.ImportEveryMinute2 * 60 * 1000;
Timer2.Elapsed += new System.Timers.ElapsedEventHandler(Timer2_Elapsed);
Timer2.Start();
}
private void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!IsActive)
{
IsActive = true;
DoSomeStuff1();
IsActive = false;
}
}
private void Timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!IsActive)
{
IsActive = true;
DoSomeStuff2();
IsActive = false;
}
}
}
Can you please tell me if volatile keyword is sufficient in my case or I should use locking or may be there is some better way for doing this?
u can use SemaphoreSlim.
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
private void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
semaphore.Wait();
DoSomeStuff1();
semaphore.Release();
}
private void Timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
semaphore.Wait();
DoSomeStuff2();
semaphore.Release();
}
With your solution it can happen that
some timed operations could not processed, because the code doesn't stop and wait till IsActive get true.
it can happened that your two operations run on the same time. Because read and write on IsActive are two operations
I suggest the lock solution.
private static object LockObj = new object();
private void Timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
lock(LockObj) { /* ... */ }
}
Not at all, on the contrary, using volatile just ensures that when having multiple threads, each of them will always see the latest value. It doesn't help in synchronizing between the threads.
The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.
The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access.
Imagine that both threads arrive at the
if (!IsActive)
They both see the latest value (false).
One advances, then the other. Now they are both inside.
If you want to thread synchronization, you'll have to use some sort of locking mechanism like lock.
Notice that with your implementation (event if it worked) the threads are not waiting for each other, they simply exit if the other one is active.
You might want to check out dataflow, it might fit your needs better.
Replace your timers with something like this:
private void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock(IsActive)
{
DoSomeStuff1();
}
}
private void Timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock(IsActive)
{
DoSomeStuff2();
}
}
Related
So i'm reading a constant serial data stream from my arduino to validate some things in my program. However displaying these lock up the UI thread. So my "solution" is to make a buffer that keeps the serial data and then use a timer to put the data on the UI thread with intervals instead of a constant stream.
My code:
public partial class ConsoleWindow : Window
{
private SerialPort _serialPort;
private List<string> bufferStrings = new List<string>();
private readonly DispatcherTimer timer = new DispatcherTimer();
public ConsoleWindow(ref SerialPort serialPort)
{
InitializeComponent();
if (serialPort != null)
{
timer.Interval = new TimeSpan(0,0,0,0,80);
timer.Tick += PopQueue;
_serialPort = serialPort;
_serialPort.DataReceived += DataReceived;
timer.Start();
}
}
private void PopQueue(object sender, EventArgs e)
{
var queue = bufferStrings;
foreach (var queueString in queue)
{
AppendText(queueString);
}
bufferStrings.Clear();
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_serialPort != null)
{
bufferStrings.Add(((SerialPort)sender).ReadLine());
//AppendText(((SerialPort) sender).ReadLine());
}
}
public void AppendText(string text)
{
Application.Current.Dispatcher.Invoke(() =>
{
if (Output.Inlines.Count > 100)
{
Output.Inlines.Remove(Output.Inlines.FirstInline);
}
Output.Inlines.Add(text);
ScrollViewer.ScrollToBottom();
});
}
}
The issue with this is that I get an exception: System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'. I know why it happens, but i have no idea how i could do this properly. And no idea of what to google either.
Here are two solutions you can take to prevent the InvalidOperationException:
Copy the buffer to a new list before iterating through its contents. You can do so by calling var queue = bufferStrings.ToList(); Note that you must include using System.Linq; to use ToList().
Make the iteration thread safe by surrounding it with the lock keyword:
private void PopQueue(object sender, EventArgs e)
{
lock(bufferStrings)
{
foreach (var queueString in bufferStrings)
{
AppendText(queueString);
}
bufferStrings.Clear();
}
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_serialPort != null)
{
lock(bufferStrings)
{
bufferStrings.Add(((SerialPort)sender).ReadLine());
//AppendText(((SerialPort) sender).ReadLine());
}
}
}
The simplest solution is to synchronize the accesses to the bufferStrings queue using a Monitor through the lock construct:
private void PopQueue(object sender, EventArgs e)
{
lock (bufferStrings)
{
foreach (var queueString in bufferStrings)
{
AppendText(queueString);
}
bufferStrings.Clear();
}
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_serialPort != null)
{
lock (bufferStrings)
{
bufferStrings.Add(((SerialPort)sender).ReadLine());
//AppendText(((SerialPort) sender).ReadLine());
}
}
}
The problem is that while you're iterating over an IEnumerable using foreach, the collection is being changed in another thread.
What you need is a collection that you can concurrently add to and read from.
At the top of the file add
using System.Collections.Concurrent;
Change this:
private List<string> bufferStrings = new List<string>();
to
private ConcurrentQueue<string> bufferStrings = new ConcurrentQueue<string>();
Change
bufferStrings.Add(((SerialPort)sender).ReadLine());
to
bufferStrings.Enqueue(((SerialPort)sender).ReadLine());
Then, you can read from the queue without worrying about whether something else is writing to it:
private void PopQueue(object sender, EventArgs e)
{
while (bufferStrings.TryDequeue(out string dequeued))
AppendText(dequeued);
}
This just keeps trying to take items out the queue until there are no more. TryDequeue returns false when the queue is empty. If you keep adding items while this method is running it will just keep processing them.
ConcurrentQueue
Reactive Extensions provide most of this functionality out of the box.
Check out Intro to RX, Observable Timer, Replacing Events, ReactiveUI
I have several textboxes in my wpf application. The LostFocus-Event of each textbox starts a backgroundworker to send the data to a connected serial port.
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
At this point, everything is working fine.
But sometimes I have to send two data-points directly after each other and there I have a problem.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
The Backgroundworker is still running and I get an exception thrown.
Is it possible to wait after the first online_mode_send_worker.RunWorkerAsync(data) until it has finished and then start the second online_mode_send_worker.RunWorkerAsync(data)?
while(online_mode_send_worker.isBusy); is not working because the main-thread is blocking and the RunWorkerCompleted() is not thrown and so the Backgroundwoker is always busy.
I have found something like this, but Application.DoEvents() is not available in wpf.
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
Here is a rough idea of what I mentioned in the comments.
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
You have a queue to hold on to messages that were sent while the background worker was busy and have the worker check the queue for any pending messages when it has completed doing its work.
The messenger can be used like this.
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
It seems that I missed the serial stuff. So what you want to do is synchronize your asynchronuouscalls:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
also see:
https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
ORIGINAL ANSWER
You can use Task instead of Backgroundworker.
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
I also would like to suggest that you make real objects instead of passing string arrays as arguments.
For Example send BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
and a corresponding method:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
I think your should use RunWorkerCompleted event and add a delegate:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
Make sure you put there a condition to avoid infinite loop.
I'd say that if you MUST wait until after the first "job" is done, that what you want is Task.ContinueWith() and change your interface accordingly. The msdn page is good for it IMO, but watch out that you're waiting on the "correct" task object. Hint: it's the return value of ContinueWith() that you should call Wait() on. This is a good pattern to do for launching a Task and then waiting for it later as long as you can keep the Task that is returned so you can wait on it.
For a more generic "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding." I would suggest using a BlockingCollection<Action> with only one thread consuming them. An example of how to do that is found in this other answer.
Update:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
Is not good aproach, because UI will be blocked on time of waiting. Better:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
Original answer:
I advice not to use construction: while (bw.Busy) { ... } (it consumes cpu time), use synchronization objects, for example, ManualResetEvent
BackgroundWorker is great class, but does not support waiting. Just create addition object for waiting:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
If you need only call twice you can do this:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
But if you need to queue commands you need rewrite in another way Using Task.
One Task where inside it you will have a for-loop where you will send your data through serial port sequentially.
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx
I have the problem with changing the timer Interval in backgroundworker's DoWork event. While changing the Interval by clicking the Button, Timer stops and doesn't start again.
Does anyone know how to solve this problem?
Simple code:
public Form1()
{
InitializeComponent();
timerTest.Tick += new EventHandler(timerTest_Tick);
timerTest.Interval = 1000;
timerTest.Start();
}
private void buttonTest_Click(object sender, EventArgs e)
{
push = true;
}
private void timerTest_Tick(object sender, EventArgs e)
{
ticks++;
labelTest.Text = ticks.ToString();
if(running == false)
{
running = true;
backgroundWorkerTest.RunWorkerAsync();
}
}
public void activate()
{
timerTest.Stop();
timerTest.Interval = 4000;
timerTest.Start();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
while(running)
{
if(push == true)
{
activate();
}
}
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
running = false;
}
}
}
You never set push to false.
Therefore, the following code:
while(running)
{
if(push == true)
{
activate();
}
}
will continuously call activate() in a tight loop. activate() stops the timer and then restarts it, and the time between calls to it will be far less than the timer interval. Therefore, the timer will never be left long enough to fire.
In any case, why don't you call activate() directly from buttonTest_Click()?
I can see this was asked a long time ago, but for the reference:
When it comes to timers or threadings in general (remember timer is system.threading) in combination with background workers (tasks) Never ever try to change a thread proerties randomly without knowing what the worker is doing.
It is always a good practice when assigning the DoWork handler to prepare the background worker Progress and Complete handlers as well.
At each cycle, report the progress or the completion, this would give you the chance to do your checks and modify another thread properties if needed.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
// do task 1 as per the timer1 interval
// do task 2 as per such and such .....
// if I call ChangeInterval here I'll be fiddling with another thread when
// this is still in progress
// that a full loop, Progress will be reported now
}
}
private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e)
{
// Now as the Do work is not in progress
// do something
// check if the user wanted to change the interval ?
// if yes then
ChangeInterval(6000);
// here the progress reporting is done so it will go back to DoWork with the
// NewInterval Value in place and the timer enabled
}
private void ChangeInterval(int NewInterval)
{
timer1.Enabled =false;
timer1.Interval = NewInterval;
timer1.Enabled = true;
}
Try invoke your activate method with the UI Thread's Dispatcher. (Assuming Win Forms?)
this.Invoke(new Action(activate));
Reason is that your timer is a UI control and you're updating the Interval on a separate thread. This will throw a Cross-Thread exception.
Why don't you see the exception? When the DoWork method in your BackgroundWorker throws an exception, it will be propogated to the Completed method. So you should always look at e.Error to see if an exception occurred.
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
// Oh no something went wrong...
}
running = false;
}
It took me a while, but I found out what was wrong. I'll post you a working code, just in case someone will have the same problem.
public partial class Form1 : Form
{
public int ticks = 0;
public bool running = false;
public bool push = false;
public Form1()
{
InitializeComponent();
timerTest.Tick += new EventHandler(timerTest_Tick);
timerTest.Interval = 1000;
timerTest.Start();
}
private void buttonTest_Click(object sender, EventArgs e)
{
push = true;
}
private void timerTest_Tick(object sender, EventArgs e)
{
ticks++;
labelTest.Text = ticks.ToString();
if(running == false)
{
running = true;
backgroundWorkerTest.RunWorkerAsync();
}
}
public void activate()
{
ZmienIntervalNaAwaryjny = true;
}
public bool ZmienIntervalNaAwaryjny = false;
private void DoWork(object sender, DoWorkEventArgs e)
{
if(push == true)
{
activate();
}
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
if(ZmienIntervalNaAwaryjny == true)
{
timerTest.Stop();
timerTest.Interval = 12000;
timerTest.Start();
}
ZmienIntervalNaAwaryjny = false;
running = false;
}
}
I have an application that takes a Wireshark capture file and feeds it (all the containing packets) into a network adapter.
Currently my application is a big mess - countless global variables & every task opened within a seperate BackgroundWorker instance etc...
To clarify - the purpose of using BackgroundWorkers here (more specifically the DoWork, RunWorkerCompleted events and the WorkerReportsProgress property) is to prevent the packet feeding operations from freezing my UI. To stop an operation, I need access to these workes - for now, global variables are used to achieve this.
So the question is - should I place my BackgroundWorker objects inside a Singleton-type class and then call this object when necessary?
From a technical point of view is possible, after all the singleton pattern is a design pattern that restricts the instantiation of a class to one object
you can try something like this
public class BackWorkerSingleton
{
private BackgroundWorker _backgroundWorker;
private static readonly object myLock = new object();
private static BackWorkerSingleton _backWorkerSingleton = new BackWorkerSingleton();
public delegate void ReportProgressEventHandler(object sender,MyEventsArgs e);
public event ReportProgressEventHandler ReportProgress = delegate{ };
private BackWorkerSingleton()
{
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
}
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.ReportProgress( this, new MyEventsArgs(){Progress = e.ProgressPercentage});
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// do your work here
}
public void StartTheJob()
{
_backgroundWorker.RunWorkerAsync();
}
public static BackWorkerSingleton Worker
{
get
{
lock (myLock)
{
if (_backWorkerSingleton == null)
{
_backWorkerSingleton = new BackWorkerSingleton();
}
}
return _backWorkerSingleton;
}
}
}
class MyEventsArgs:EventArgs
{
public int Progress { get; set; }
}
and here the report progress
private void Form1_Load(object sender, EventArgs e)
{
BackWorkerSingleton.Worker.ReportProgress += new BackWorkerSingleton.ReportProgressEventHandler(Worker_ReportProgress);
}
void Worker_ReportProgress(object sender, MyEventsArgs e)
{
}
and call it like this
BackWorkerSingleton.Worker.StartJob()
I want to access the thread from other function in the same class.
For example
private void timer1_Tick(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ThreadStart(Send1));
thread1.Start();
}
private void stop_btn_Click(object sender, EventArgs e)
{
thread1.Stop();
}
I wan to access thead1 from stop_btn_Click event. Both functions are in the same class Form1.
Declare private Thread thread1; on the class level rather than method
class ClassName
{
private Thread workerThread = null;
private void timer1_Tick(object sender, EventArgs e)
{
this.workerThread = new Thread(new ThreadStart(Send1));
workerThread.Start();
}
private void stop_btn_Click(object sender, EventArgs e)
{
this.workerThread.Stop();
}
}
By looking at the method name timer1_Tick() I can assume that you are simulating a timer behaviour. Take a look at the System.Timers.Timer and System.Threading.Timer classes perhaps you'll find them more useful for your case.
You need to store the thread in a private field in the form.
You also need to figure out what should happen if the user clicks Start twice; you may want to check whether the thread is already running, or use a list of threads.
you could take the variable outside the method (moving it into the class as a field):
private Thread thread1 = null;
void timer1_Tick(object sender, EventArgs e)
{
thread1 = new Thread(new ThreadStart(Send1));
thread1.Start();
}
private void stop_btn_Click(object sender, EventArgs e)
{
if (thread1 != null)
thread1.Stop();
}