lock statement in event handler - Possible deadlock? - c#

Can this code provocate a deadlock? I am afraid that the handler is executed suddenly in the middle of the execution of the DoWork function (which is also locked).
private object lockObject = new object();
public void DoWork()
{
lock(lockObject)
{
ChannelFactory<TChannel> channelFactory = CreateChannelFactory();
channelFactory.Faulted += (sender, e) => OnChannelFactoryFaulted(sender, e);
// Some code.. access to dictionary...
}
}
private void OnChannelFactoryFaulted(object sender, EventArgs e)
{
lock (lockObject)
{
// Some code.. access to dictionary..
}
}
Thanks

Related

Iterate over constantly modified List

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

wpf c# backgroundworker wait until finished

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

How to stop a thread for a time consuming function

I'm trying to do a windows app and I have a function that takes several minutes to complete the task. I have a start button and
I'd like to add a stop button in order to stop the processing of the function whenever I want to stop it.
I'm trying with the code below, but I'm not sure how to abort the Thread1 inside btnStop since Thread1 is marked as "does not exists
in current context".
May you please suggest me/point me in rigth direction in how would be a good way to do this. Thanks in advance.
namespace SampleStartStop
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
Thread Thread1 = new Thread(SlowFunction);
Thread1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
Thread1.Abort();
MessageBox.Show("Processing canceled");
}
public void SlowFunction()
{
var end = DateTime.Now + TimeSpan.FromSeconds(10);
while (DateTime.Now < end)
{ }
MessageBox.Show("Process finished");
}
}
}
Update:
Hi KCdod, thanks for your help, When I only declare thread as global variable I get "An unhandled exception of type 'System.NullReferenceException'
occurred in SampleStartStop.exe".
Hi Alexei, thanks for the correction. Thanks zerkms and Alexei for share about cancellation tokens. Following the example in link you shared
I was able to write the code below. It seems to work but I'd like the approbal of you experts if it needs some change or if it is fine.
The only doubt regarding the current code is, if Stop button is pressed it stops the processing fine, but if I click start button again,
nothing happens and I need to close and open again the App in order to get working again start button, is this normal?
The other doubt is in the part inside "The listener". In MSDN example they put "// Perform cleanup if necessary.", so, what kind of clean up
they are talking about?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Create the token source.
CancellationTokenSource cts = new CancellationTokenSource();
private void btnStart_Click(object sender, EventArgs e)
{
// Pass the token to the cancelable operation.
ThreadPool.QueueUserWorkItem(new WaitCallback(SlowFunction), cts.Token);
}
private void btnStop_Click(object sender, EventArgs e)
{
// Request cancellation.
cts.Cancel();
// Cancellation should have happened, so call Dispose.
cts.Dispose();
MessageBox.Show("Processing canceled");
}
public void SlowFunction(object obj)
{
CancellationToken token = (CancellationToken)obj;
var end = DateTime.Now + TimeSpan.FromSeconds(10);
while (DateTime.Now < end)
{
// Thread 2: The listener
if (token.IsCancellationRequested)
{
// Perform cleanup if necessary.
//...
// Terminate the operation.
break;
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
}
Update:
Thanks Alexei for your correction, I've modified the code with your suggestions and this time works nice. the code is as below. I only have an issue since in my real code, the Function needs a string argument to work and I don't know how to call it inside the part "WaitCallback(SlowFunction)" and how to define the function in the code, since here is defined like this "public void SlowFunction(object obj) {...}" and in my real function is like this "public void SlowFunction(string str)". I think that I'd need to ask in a new question this issue.
namespace SampleStartStop
{
public partial class Form1 : Form
{
// Create the token source.
CancellationTokenSource cts = new CancellationTokenSource();
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
// Pass the token to the cancelable operation.
cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(SlowFunction), cts.Token);
}
private void btnStop_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
MessageBox.Show("Processing canceled");
}
}
public void SlowFunction(object obj)
{
CancellationToken token = (CancellationToken)obj;
var end = DateTime.Now + TimeSpan.FromSeconds(5);
while (DateTime.Now < end)
{
if (token.IsCancellationRequested)
{
break;
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
}
}
There is no good way to terminate thread that is not cooperating. Indeed Thread.Abort will do that but at price of potentially leaving non-disposed objects and abandoned synchronization primitives thus potentially destabilizing your program.
Fix for your immediate problem - move Thread1 to be class level member instead of local variable. You'll need to check if it is already set/cleared:
public partial class Form1 : Form {
...
Thread thread1 = null;
private void btnStart_Click(object sender, EventArgs e)
{
if (thread1 != null)
{
thread1.Abort();
}
thread1 = new Thread(SlowFunction);
Thread1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
if (thread1 != null)
{
thread1.Abort();
thread1 = null;
MessageBox.Show("Processing canceled");
}
}
It will be much better if you can make "slow function" to cooperate in termination - i.e. by periodically checking some value. Check Cancellation tokens for .Net way of doing so.
You can declare your thread, Thread Thread1; as global variable. In your current code your Thread1 scope limits to btnStart_Click() event function.
namespace SampleStartStop
{
public partial class Form1 : Form
{
Thread Thread1=null;
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
Thread1 = new Thread(SlowFunction);
Thread1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
Thread1.Abort();
MessageBox.Show("Processing canceled");
}
public void SlowFunction()
{
var end = DateTime.Now + TimeSpan.FromSeconds(10);
while (DateTime.Now < end)
{ }
MessageBox.Show("Process finished");
}
}
}
Additional - Thread abort is not GOOD but you can use it.

Should I put a BackgroundWorker inside a Singleton?

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()

c#: controlling access to object from different threads

How do I control when a thread is permitted to access an object and when it is not.
For example, if I have situation like below, I want to make sure that when I am doing something with objFoo in my ButtonClick event, I should not be able to touch objFoo from my doSomethingWithObjFoo method.
private void button1_Click(object sender, EventArgs e) {
// doing something with objFoo
}
private void timer1_Tick(object sender, EventArgs e) {
Thread T = new Thread(new ThreadStart(doSomethingWithObjFoo));
T.Start();
}
private void doSomethingWithObjFoo(){
// doing something else with objFoo
}
The easiest way is perhaps to use lock:
private object _fooLock = new object();
private void button1_Click(object sender, EventArgs e) {
lock(_fooLock)
{
// doing something with objFoo
}
}
private void timer1_Tick(object sender, EventArgs e) {
Thread T = new Thread(new ThreadStart(doSomethingWithObjFoo));
T.Start();
}
private void doSomethingWithObjFoo(){
lock(_fooLock)
{
// doing something else with objFoo
}
}
There are other options as well, such as using a ReaderWriterLockSlim.
That what we use lock for.
Thread Synchronization is a must read.
public class TestThreading
{
private System.Object lockThis = new System.Object();
public void Process()
{
lock (lockThis)
{
// Access thread-sensitive resources.
}
}
}

Categories

Resources