I have some threads running and once an error is detected I wanna use an interaction like messagebox to continue execution or stop it. And I don't want multiple msgboxes on my screen so I added a semaphore to let only 1 thread do the job. But it doesn't work.
so I have the following situation:
private void DO_WORK()
{
//some code missing
lock (_threadLock)
{
++activeWorkers;
}
ThreadPool.QueueUserWorkItem(o =>
{
WorkRequests(result);
lock (_threadLock)
{
--activeWorkers;
Monitor.Pulse(_threadLock);
}
});
lock (_threadLock)
{
if (STOP)
break;
while (activeWorkers > THREADS)
Monitor.Wait(_threadLock);
}
}
private void WorkRequests(string mystr)
{
string source = null;
string result = null;
bool processed = false;
bool messageBoxShown = false;
///////////////////////////////////
while(true)//this is for rechecking the bad ones
{
source = GetSource(mystr);
if (source == "ERROR_LIMITED")
{
lock (_threadLock)
{
if (!messageBoxShown)//<--- check messageBoxShown
{
if (MessageBox.Show("Blocked IP detected!\nPlease change it!", "test program", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
{
messageBoxShown = true; //<--- set to true
}
else
DoStop();
}
}
}
else
break;
}
result = mystr + " - " + source;
////////////////////////////////////////
}
How can I pause all threads except one which will show the messagebox and based on dialogbox to continue execution or stop it?
Your primary problem is that messageBoxShown is a local variable, so each thread is going to have its own copy of it. One thread setting it to true will not be seen by the other threads.
If you want all threads to be able to see it, you have to declare it at class scope:
private volatile bool messageBoxShown = false;
private void WorkRequests(string mystr)
{
// other stuff
lock (_threadLock)
{
if (messageBoxShown)
{
return;
}
}
// do dialog stuff, then
messageBoxShown = true;
}
Also, in your code you have:
if (!messageBoxShown)//<--- check messageBoxShown
{
if (MessageBox.Show("Blocked IP detected!\nPlease change it!", "test program",
MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
{
messageBoxShown = true; //<--- set to true
}
else
DoStop();
}
If the user presses Cancel then messageBoxShown is never set to true. So every thread will display the message box unless you have some other means to stop them from doing it.
Actually the problem in you code is you doesn't stop the other threads or ask it to skip showing MessageBox Your code actually limits number of threads can execute
MessageBox block to 1, So they start showing ony by one.
Instead try something like this
private volatile bool messageBoxShown = false;//Declare a Instance variable
var source = validate(mystr);
if (source == "ERROR")
{
lock (_threadLock)
{
semaf.WaitOne();
}
if(messageBoxShown)<--- check messageBoxShown
{
return;// or skip showing messagebox do whatever you want
}
if (MessageBox.Show("Blocked IP detected!\nPlease change it!", "test program", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
{
lock (_threadLock)
{
messageBoxShown = true; <--- set to true
semaf.Release();
}
}
else
DoStop();
}
Hope this will solve your problem.
Related
This is a bit difficult to explain. I have a thread instantiated within a WindowsForm, the thread is not a priority and does its job if nobody has taken the resource exclusively using Monitor.TryEnter.
But when closing the form I use the FormClosing event to stop the processing, save the processed data before closing, etc. When trying to take the resource with another Monitor.Enter .. the block is produced.
But the funny thing is that in the following way it works!!. Using a new/secondary thread or task to block the internal thread and perform the final job of save buffering.
"Minimal, Complete, and Verifiable example:"
namespace WindowsFormsApplication3
{
using System;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class Form1 : Form
{
private readonly object _syncRoot = new object();
private volatile bool _working = false;
readonly Label lblInfo = new Label();
public Form1()
{
InitializeComponent();
SuspendLayout();
lblInfo.Text = "...";
lblInfo.Location = new Point(0, 0);
Controls.Add(lblInfo);
CheckBox chkBox = new CheckBox();
chkBox.Text = "Use Task inside FormClosing";
chkBox.AutoSize = true;
chkBox.Location = new Point(Width / 2 - chkBox.Width / 2, Height / 2 + chkBox.Height);
chkBox.Click += ChkBox_Click; ;
Controls.Add(chkBox);
ResumeLayout(false);
FormClosing += Form1_FormClosing;
}
private CancellationTokenSource _cancellationTokenS;
private bool _flUseThread = false;
private void Form1_Load(object sender, EventArgs e)
{
_cancellationTokenS = new CancellationTokenSource();
CancellationToken token = _cancellationTokenS.Token;
_working = true;
Task.Run(() =>
{
while (_working)
{
if (token.IsCancellationRequested)
break;
processData(DateTime.Now);
}
}, token);
}
private void ChkBox_Click(object sender, EventArgs e)
{
_flUseThread = ((CheckBox)sender).Checked;
}
private bool _alreadySafedClose = false;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_flUseThread)
{
if (_alreadySafedClose) return;
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
e.Cancel = true; // Avoid close form.
Enabled = false; // Avoid interact with form.
Task.Factory.StartNew(() =>
{
bool lockWasTaken = false;
try
{
_working = false;
// Lock other thread.
Monitor.Enter(_syncRoot, ref lockWasTaken);
_cancellationTokenS.Cancel();
saveBufferResults();
MessageBox.Show("OK with Thread");
}
catch (Exception)
{
// ignored
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
Invoke((MethodInvoker)delegate
{
_alreadySafedClose = true;
Close(); //Finally Close.
});
}
});
}
else
e.Cancel = true;
}
else
{
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
bool lockWasTaken = false;
try
{
_working = false;
_cancellationTokenS.Cancel();
// Lock other thread avoid continue processing.
Monitor.Enter(_syncRoot, ref lockWasTaken); // <------ here it stays blocked!!
saveBufferResults();
MessageBox.Show("OK");
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
}
}
else
e.Cancel = true;
}
}
private void processData(DateTime data)
{
//flag to reduce locks..
if (!_working) return;
if (Monitor.TryEnter(_syncRoot))
{
try
{
//To escape quickly
if (_working)
innerProcessFrame(data);
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
Monitor.Exit(_syncRoot);
}
}
}
private ulong _dataCounter = 0;
private void innerProcessFrame(DateTime data)
{
Thread.Sleep(100);
Invoke((MethodInvoker)delegate
{
lblInfo.Text = data.Millisecond + " " + _dataCounter;
});
_dataCounter++;
}
private void saveBufferResults()
{
Thread.Sleep(1);
}
}
}
I believe that FormClosing is breaking the synchronization context or blocking the thread of work and
therefore in the event of the thread of work it never reaches the line where it releases the resource
using Monitor.Exit, in the processData(...) method:
My question is why does this happen only when closing the form within the FormClosing event?, because
if I put a button on the form and put the same blocking code everything stops correctly there is no DeadLock, and why does it work creating a secondary thread to do the final work when closing the form ?, and finally if someone knows a better or correct way to solve it.
I'm having a problem when I' trying to show a progress bar until a external process ends which I started for the WPF window.
The code is like this:
private void Button1_Click(object sender, RoutedEventArgs e)
{
Button1.IsEnabled = false;
Button1.Content = "Please Wait";
ProgressBar1.Visibility = Visibility.Visible;
if (a == 1 && b == 0)
{
var processStartInfo = new ProcessStartInfo(#"External Process Path 1");
processStartInfo.Verb = "runas";
try
{
Process.Start(processStartInfo);
}
catch (Win32Exception ex)
{
MessageBox.Show(ex.ToString(), "Run As",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
if (b == 1 && a == 0)
{
var processStartInfo = new ProcessStartInfo(#"External Process Patch 2");
processStartInfo.Verb = "runas";
try
{
Process.Start(processStartInfo);
}
catch (Win32Exception ex)
{
MessageBox.Show(ex.ToString(), "Run As",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
Button2.IsEnabled = true;
ProgressBar1.Visibility = Visibility.Hidden; //This is what I want to toggle after process ends
}
I've tried Thread.Sleep(time) method and also for-loop but nothing seems to work.
I'm very new in WPF. So, please try to be a little brief.
Thanks,
D.K.
do you know how long the external process lasts? if you don't you can try to set the IsIndeterminate property to true on your progress bar. this will show a continuous animation. When your process returns you can set it to false again in order to stop the animation.
also, in your code, i think you're not waiting for the process to finish. you could do this by using the code below.
Process p = Process.Start("IExplore");
p.WaitForExit();
please note that WaitForExit() blocks the current thread. as a result the app will stop responding. to keep the UI responsive you might like to start your process on a different thread like below.
private void onClick_Handler(object sender, EventArgs e) {
//disable button here
Task.Factory.StartNew(() => {
Process p = Process.Start("IExplore");
p.WaitForExit();
//enable button here. make sure to do this on the UI thread
//since you're doing this in the code-behind you should have access
//to the dispatcher
Dispatcher.BeginInvoke((Action)OnUpdateUI);
});
}
private void OnUpdateUI(){
}
In the above code, you are starting a process but not waiting for it to end, so the debugger when executes your:
Process.Star("Add Some Task");
, it jumps on to the next statement which is
button2.IsEnabled = true;
and so on. As a result your ProgressBar1 is not visible to you.
Please wait for the Process to end first. Write
Process.WaitForExit();
just after your statement
Process.Start("Your already started task");
Also you can create an Asynchronous thread to run in parallel.
Example:
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
taskA.Start();
taskA.Wait();
Also in your above code, you are only displaying ProgressBar but not updating its value with time. So as a result ProgressBar will be visible only with initial value.
For ProgressBar, do something like,
ProgressBar ProgressBar1 = new ProgressBar();
ProgressBar1.Maximum = 100;
ProgressBar1.Minimum = 0;
Task.Start();
if( Task.Status == "Running")
{
ProgressBar1.Value = 50;
}
if( Task.Status == "completed")
{
ProgressBar1.Value =100;
}
else
{
ProgressBar.Value=0;
Task.Wait();
}
The code above mentioned may not be syntactically right. So do look for correct syntax.
I have a winform application that runs in background with a BackgroundWorker that has an infinite loop that execute something every hour. My UI Form class is something like this:
public partial class frmAutoScript : Form
{
private volatile bool _isDownloading = false;
private bool IsDownloading { get { return this._isDownloading; } set { this._isDownloading = value; } }
public frmAutoScript()
{
InitializeComponent();
this.RunAutoSynchronization();
}
private void RunAutoSynchronization()
{
bool isDownloading = this.IsDownloading;
BackgroundWorker bgwDownloader = new BackgroundWorker();
bgwDownloader.WorkerReportsProgress = true;
bgwDownloader.ProgressChanged += (sndr, evnt) =>
{
if (evnt.ProgressPercentage == 2)
isDownloading = this.IsDownloading;
else
{
this.IsDownloading = evnt.ProgressPercentage == 1;
isDownloading = this.IsDownloading;
}
};
bgwDownloader.DoWork += (sndr, evnt) =>
{
while (true)
{
if (DateTime.Now.Hour == 16 &&
DateTime.Now.Minute == 0)
{
try
{
bgwDownloader.ReportProgress(2);
if (!isDownloading)
{
bgwDownloader.ReportProgress(1);
new Downloader().Download();
}
bgwDownloader.ReportProgress(0);
}
catch { }
}
System.Threading.Thread.Sleep(60000);
}
};
bgwDownloader.RunWorkerAsync();
}
}
And in that frmAutoScript, I also have a button named btnDownload that when clicked, it will download and change the value of the volatile varialbe _isDownloading. The event of the button is something like this:
private void btnDownload_Click(object sender, EventArgs e)
{
if (IsDownloading)
MessageBox.Show("A download is currently ongoing. Please wait for the download to finish.",
"Force Download", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
else
{
this.IsDownloading = true;
BackgroundWorker bgwDownloader = new BackgroundWorker();
bgwDownloader.DoWork += (sndr, evnt) =>
{
try
{
new Downloader().Download();
}
catch(Exception ex)
{
MessageBox.Show("An error occur during download. Please contact your system administrator.\n Exception: " +
ex.GetType().ToString() + "\nError Message:\n" + ex.Message + " Stack Trace:\n" + ex.StackTrace, "Download Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
bgwDownloader.RunWorkerCompleted += (sndr, evnt) =>
{
this.IsDownloading = false;
};
bgwDownloader.RunWorkerAsync();
}
}
But when I click the button btnDownload and the _isDownloading is set to true, and when the system time hit the 4:00 PM, the new Downloader().Download(); is executed again eventhough the _isDownloading is set to true. Why was it like this?
My code is in C#, framework 4, project is in winforms, build in Visual Studio 2010 Pro.
Your code is not testing against the volatile field - it is testing against isDownloading, which looks like a "local", but (because it is captured) is in fact a regular (non-volatile) field. So: either use some kind of memory barrier, or force that to be a volatile read. Or more simply: get rid of isDownloading completely, and check against the property.
Incidentally, the cache-defeating properties of volatile are not the intent of the keyword, but rather: a consequence. It'll work, but personally I'd suggest writing the code to work by intent rather than by consequence, perhaps using either a simple lock or something like Interlocked.
I have a MessageBox.Show event that I want to also prevents timer-based methods from running while the MessageBox remains open.
Here is my code (Changes the value in a file location on a network every x minutes):
public void offlineSetTurn()
{
try
{
using (StreamWriter sWriter = new StreamWriter("FileLocation"))
{
sWriter.WriteLine(Variable);
}
}
catch (Exception ex)
{
DialogResult result = MessageBox.Show("Can't find file. Click Okay to try again and Cancel to kill program",MessageBoxButtons.OKCancel);
if (result == DialogResult.OK)
{
offlineSetTurn();
}
else if (result == DialogResult.Cancel)
{
Application.Exit();
}
}
}
I have methods in the form that are calling this every thirty seconds. Meaning every thirty seconds, another MessageBox pops up. Is there a way to pause the application with MessageBox and if not, what would be the best way to resolve this issue? If possible, I'd like to avoid using Timer.Stop() as it would reset the Timer count.
The simplest solution is to have a flag indicating whether or not the message box is currently open:
private bool isMessageBoxOpen = false;
public void offlineSetTurn()
{
if (isMessageBoxOpen)
return;
try
{
using (StreamWriter sWriter = new StreamWriter("FileLocation"))
{
sWriter.WriteLine(Variable);
}
}
catch (Exception ex)
{
isMessageBoxOpen = true;
DialogResult result = MessageBox.Show("Can't find file. Click Okay to try again and Cancel to kill program",MessageBoxButtons.OKCancel);
isMessageBoxOpen = false;
if (result == DialogResult.OK)
{
offlineSetTurn();
}
else if (result == DialogResult.Cancel)
{
Application.Exit();
}
}
}
C# 2005
I am using a background worker to process some login information. However, the background worker has to stop and wait for 2 events to happen. Once these have finished the background worker can complete its job. They are callbacks that will call the Set() method of the AutoResetEvent.
So I am using AutoResetEvent to set when these 2 events have finished. However, I seemed to be getting this error message:
"Exception has been thrown by the target of an invocation."
And Inner exception
Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index".
The exception usually fires when the registration success leaves scope.
Many thanks for any advice
The code for the background worker.
// Waiting for 'Account in use' and 'Register success or failure'
AutoResetEvent[] loginWaitEvents = new AutoResetEvent[]
{
new AutoResetEvent(false),
new AutoResetEvent(false)
};
private void bgwProcessLogin_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Wait until event is set or timeout");
loginWaitEvents[0].WaitOne(3000, true);
if (this.accountInUseFlag)
{
if (this.lblRegistering.InvokeRequired)
{
///this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(this.UpdateRegisterLabel), "Account in use");
}
else
{
///this.lblRegistering.Text = "Account in use";
}
// Failed attemp
e.Cancel = true;
// Reset flag
//this.accountInUseFlag = false;
return;
}
else
{
// Report current progress
//this.bgwProcessLogin.ReportProgress(7, "Account accepted");
}
Console.WriteLine("Just Wait the result of successfull login or not");
loginWaitEvents[1].WaitOne();
Console.WriteLine("Results for login registionSuccess: [ " + registerSuccess + " ]");
if (this.registerSuccess)
{
// Report current progress
//this.bgwProcessLogin.ReportProgress(7, "Register Succesfull");
// Reset flag
//this.registerSuccess = false;
}
else
{
if (this.lblRegistering.InvokeRequired)
{
//this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(this.UpdateRegisterLabel), "Failed to register");
}
else
{
// this.lblRegistering.Text = "Failed to register";
}
// Failed attemp
e.Cancel = true;
return;
}
}
// Wait for the callback to set the AutoResetEvent
// Error sometimes happens when the function leaves scope.
private void VaxSIPUserAgentOCX_OnSuccessToRegister(object sender, EventArgs e)
{
Console.WriteLine("OnSuccessToRegister() [ Registered successfully ]");
this.registerSuccess = true;
this.loginWaitEvents[1].Set();
}
// If the flag is not set, then just time out after 3 seconds for the first LoginWaitEvent.waitOne(3000, true)
private void VaxSIPUserAgentOCX_OnIncomingDiagnostic(object sender, AxVAXSIPUSERAGENTOCXLib._DVaxSIPUserAgentOCXEvents_OnIncomingDiagnosticEvent e)
{
string messageSip = e.msgSIP;
//Indicates that a user is already logged on (Accout in use).
string sipErrorCode = "600 User Found";
if (messageSip.Contains(sipErrorCode))
{
// Set flag for account in use
this.accountInUseFlag = true;
Console.WriteLine("OnIncomingDiagnostic() WaitEvent.Set() accountInUseFlag: " + this.accountInUseFlag);
loginWaitEvents[0].Set();
}
}
There is most likely an indexing error in the UpdateRegisterLabel method.
Get a Stack Trace from the inner exception, it should point you more closely to where it is.