Block concurrent threads only when Windows was closed - c#

I have code
private void AbrirConexao(string strConexao)
{
try
{
conexao = new NpgsqlConnection(strConexao);
conexao.Open();
}
catch (Exception)
{
ReconectarDB(null, strConexao);
}
}
public bool ReconectarDB(string strConexao)
{
bool erroConexao = false;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (obj, ea) =>
{
int erro = 0;
while (erro <= 4)
{
Thread.Sleep(1000);
try
{
conexao = new NpgsqlConnection(strConexao);
conexao.Open();
erroConexao = false;
break;
}
catch
{
erro++;
erroConexao = true;
}
}
};
bw.RunWorkerCompleted += (obj, ea) =>
{
if (erroConexao)
DialogReconectando.AlterarTela(ErroConexao.SemConexao);
else
DialogReconectando.Close();
};
bw.RunWorkerAsync();
Application.Current.Dispatcher.Invoke(new Action(() =>
{
if (DialogReconectando == null || Conexao.DialogReconectando.IsLoaded == false)
DialogReconectando = new DialogErroConexao(ErroConexao.Reconectando);
if(DialogReconectando.ShowActivated)
{
DialogReconectando.ShowActivated = true;
DialogReconectando.ShowDialog();
}
}));
return erroConexao;
}
I am using the Open Connection method to connect to the database. and when the connection fails, ReconnectDB is trying to reconnect with the database. If it fails, a Window is opened that there are two buttons, Retry and Abort the System.
The problem is that there are situations that I use other concurrent threads that makes requests with the database. In those cases, I would not want it to display a new Window. So if there is a Window open, I would like the Thread to lock until the Window is closed. I tried to solve the problem using EventWaitHandle. However, Window is also caught in this situation. Would you have any idea how you could solve this problem?

It depends on what you want the second thread to do. If you want the second thread to simply skip over showing the window, you can use a semaphore to ensure that only a single thread shows the window, like this:
SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
private void ShowWindowNonBlocking()
{
bool acquiredLock = false;
try
{
acquiredLock = semaphore.Wait(0);
if (acquiredLock)
{
// This thread now has exclusive access to the isWindowShown variable
var result = MessageBox.Show(
"Retry the connection?",
"Connection Failed",
MessageBoxButtons.RetryCancel);
if (result == DialogResult.Retry)
{
// Retry the connection
}
}
else
{
// Another thread is showing the window
}
}
finally
{
if (acquiredLock)
{
semaphore.Release();
}
}
}
Here is a good site that I refer back to from time to time on locking mechanisms: http://www.albahari.com/threading/part2.aspx#_Semaphore
If however, you want the second thread to block until the first window is finished (for example, if you want to know what the result of the window was in order to know whether to retry on the second thread), you can use a lock, like this:
private object windowLock = new object();
private void ShowWindowBlocking()
{
lock (windowLock)
{
var result = MessageBox.Show(
"Retry the connection?",
"Connection Failed",
MessageBoxButtons.RetryCancel);
if (result == DialogResult.Retry)
{
// Retry the connection
}
}
}
If you need further clarification, let me know and I will try to expand the answer.

Related

Multithreaded Client

I created a client-server in C#, and everything works, and I thought of adding the ability to cancel requests that timed out, so at the server-side, each request I send starts a new Timer, and when it runs out of time, I send another request that says ABORT {MessageId}.
This is what I implemented so far, but for some reason, it won't terminate the Thread, when I debugged I saw that it finds the correct thread but doesn't terminate it.
Perhaps there is even a better way for implementing this...
Dictionary<int, Thread> threads = new();
private static void ReceiveResponse()
{
//I get the message (body)
string? response = Util.Bytes2String(body);
if (response == null)
return;
//There is no need to create a new Thread for aborting a Thread...
if (ShouldAbort(response))
return;
Thread thread = new(() =>
{
HandleCommand(response, msgId);
Thread.CurrentThread.IsBackground = true;
});
threads.Add(msgId, thread);
thread.Start();
Console.WriteLine($"Thread {thread.ManagedThreadId} was created for Request No. {msgId}");
}
private static bool ShouldAbort(string command)
{
if (!Util.IsStringValid(command))
return false;
if (command.StartsWith(MessageType.ABORT_TASK))
{
if (int.TryParse(command.Split('\t')[1], out int id)) //Get the ID of the message to abort
{
if (threads.ContainsKey(id))
{
AbortThreadAt(id);
return true;
}
else
Console.WriteLine($"Couldn't abort Request No. {id} because it doesn't exist");
return false;
}
{
Console.WriteLine($"Unable to stop the Thread.");
return true;
}
}
return false;
}
private static void AbortThreadAt(int i)
{
try
{
Thread thread = threads[i];
threads.Remove(i);
Console.WriteLine($"Thread {thread.ManagedThreadId} for Request No. {i} was aborted");
thread.Interrupt();
}
catch (Exception) { }
}

How can I synchronously access/call an object from multiple background workers

I have an object on my main UI thread which sends serial commands to an arduino and I need to be able to access this object and have it send serial commands to the arduino in the order they are received from within two separate background workers.
I have searched around and found many methods to update a UI from multiple backgroundworkers but the problem is most of the solutions are geared at objects that were designed with multi threading in mind. I.e. you can do "GUIobject.Invoke(etc)".
var driver = Periphrials.InitializeArduinoDriver();
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{ break; }
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
} while (true);
});
The "driver.send......" part is what needs to be ran on the main thread but called from within the background workers.
If you are using the Backgroundworker-Class then you can use the ProgressChanged-Event. This approach does not block the backgroundworker thread with each send-call.
var driver = Periphrials.InitializeArduinoDriver();
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.WorkerReportsProgress = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{
break;
}
StillMonitor.ReportProgress(0); //Invokes the ProgressChanged Event on the thread the backgroundworker was created on.
} while (true);
});
StillMonitor.ProgressChanged += (sender, e) => {
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
}
If the thread should block with each send-call consider using the Dispatcher-Class (you need to add a reference to WindowsBase.dll):
using System.Windows.Threading; //WindowsBase.dll
//...
var driver = Periphrials.InitializeArduinoDriver();
Dispatcher driverDispatcher = Dispatcher.CurrentDispatcher; //Gets the Dispatcher for the current Thread (or creates it)
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{
break;
}
driverDispatcher.Invoke(new Action(() => { //Invoke and block the Dispatcher
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
}));
} while (true);
});
you can synchronize the driver.send call with a locking mechanism such as lock-statement.
/////class
{
private readonly object lockObject = new object();
....
///method
var driver = Periphrials.InitializeArduinoDriver();
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{ break; }
lock (lockObject)
{
//code here will run synchronously
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
}
} while (true);
});

Blocking UI thread dosen't render UI components of an other window

In my application I have a scenario where I need to block the UI Thread when I perform an action. While its blocked I have an other window that needs to show a message saying "performing action please wait" when the UI is blocked. Problem is the window shows up but it never displays the message. Please help. Even DoEvents() dosen't help. It just so lightly displays the message
Note:
I don't want the method as async because I want to block the UI when it runs.
I don't want to say ShowDialog() because it just blocks there.
private void ViewModel_PerformPrimeAction(InstrumentAction Action)
{
bool abort = false;
CommandRunningWindow cmdDialog = null;
if (Action == InstrumentAction.Prime)
{
if (Xceed.Wpf.Toolkit.MessageBox.Show((string)TryFindResource("ConfirmPrimeInstrument"),
ApplicationSettingsViewModel.Instance.ProductName, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
this.IsEnabled = false;
//This below line never shows the message.
cmdDialog = ShowCommandWindow(ViewModelsHelper.GetResourceString("PerformingPrime"));
System.Windows.Forms.Application.DoEvents();
}
UIUtils.OverrideCursor = System.Windows.Input.Cursors.Wait;
try
{
// This operation takes 10 seconds
QXInstrumentViewModel.Instance.Prime(() => { if (abort) throw new RunAbortedException(null); });
}
catch (RunAbortedException)
{
errorMessage = (string)TryFindResource("CompletePrimeInstrumentAborted");
cmdDialog?.Close();
}
catch (Exception ex)
{
var message = QXInstrumentViewModel.ToErrorCode(ex);
TokenSource = new System.Threading.CancellationToken(true);
if (message != null)
{
errorMessage = string.Format((string)TryFindResource("CompletePrimeInstrumentWithError"), Convert.ToInt32(message), errorMessage);
}
else
{
errorMessage = (string)TryFindResource("CompletePrimeInstrumentWithUnknownError");
}
cmdDialog?.Close();
}
UIUtils.OverrideCursor = null;
this.IsEnabled = true;
}
public CommandRunningWindow ShowCommandWindow(string message)
{
CommandRunningWindow cmdDialog = new CommandRunningWindow();
cmdDialog.Message = message;
cmdDialog.Owner = WPFUtils.GetActiveWindow();
cmdDialog.Show();
return cmdDialog;
}
The CommandRunningWindow has a dependency property of type string (message) that is bound to a textblock.
You can't both block and show a message on the same thread simultaneously.
What you should do is to execute your long-running operation on a background thread - the easiest way to do this is to start a TPL task - and display the message on the UI thread. You may still disable the window. Just make sure that you don't touch the UI on the background thread where your long-running operation executes.
private void ViewModel_PerformPrimeAction(InstrumentAction Action)
{
bool abort = false;
CommandRunningWindow cmdDialog = null;
if (Action == InstrumentAction.Prime)
{
if (Xceed.Wpf.Toolkit.MessageBox.Show((string)TryFindResource("ConfirmPrimeInstrument"),
ApplicationSettingsViewModel.Instance.ProductName, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
this.IsEnabled = false;
//This below line never shows the message.
cmdDialog = ShowCommandWindow(ViewModelsHelper.GetResourceString("PerformingPrime"));
}
UIUtils.OverrideCursor = System.Windows.Input.Cursors.Wait;
Task.Factory.StartNew(() =>
{
// This operation takes 10 seconds
QXInstrumentViewModel.Instance.Prime(() => { if (abort) throw new RunAbortedException(null); });
})
.ContinueWith(task =>
{
if (task.IsFaulted)
{
if (task.Exception != null && task.Exception.GetBaseException() is RunAbortedException)
{
var message = QXInstrumentViewModel.ToErrorCode(ex);
TokenSource = new System.Threading.CancellationToken(true);
if (message != null)
{
errorMessage = string.Format((string)TryFindResource("CompletePrimeInstrumentWithError"), Convert.ToInt32(message), errorMessage);
}
else
{
errorMessage = (string)TryFindResource("CompletePrimeInstrumentWithUnknownError");
}
}
else
{
errorMessage = (string)TryFindResource("CompletePrimeInstrumentAborted");
}
}
cmdDialog?.Close();
UIUtils.OverrideCursor = null;
this.IsEnabled = true;
}, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}

how to make progress bar work with functions in button event

i am new in C# so please be patient with me .
i want to make progress bar work with any functions i make in my program
i have class to check if INTERNET available and the connection of database status
and i have "progressBar1" , style is "Marquee"
i just want to indicate that there is a process work "Function" in the program and i don't need to have step or timer to increment it
just make the progress work until the function finish its code and the functions will work in button event (when i push button)
this is my code
class checkInternet
{
[DllImport("wininet.dll")]
private extern static bool InternetGetConnectedState(out int Description, int ReservedValue);
public bool checkInternetAvailable()
{
int Desc;
bool result = false;
if (InternetGetConnectedState(out Desc, 0) == true)
{
try
{
dbConnection StartConn = new dbConnection();
SqlConnection MyConnetion = StartConn.GetConnection();
MyConnetion.Open();
if (MyConnetion.State == ConnectionState.Open)
{
result = true;
}
MyConnetion.Close();
}
catch (Exception)
{
result = false;
MessageBox.Show("The database connection does not available, May be because of this reasons: \n\n1- there is a new version of the program avalible. \n2- database has some maintenance. \n\n Please check later :)", "Conection status");
}
}
else
{
result = false;
MessageBox.Show("No internet connection avalible , Please check later :) \nThanks.", "Conection status");
}
return result;
}
}
and this is what i have in my button event
private void button1_Click(object sender, EventArgs e)
{
checkInternet check = new checkInternet();
progressBar1.Value = 0;
do
{
progressBar1.PerformStep();
} while (check.checkInternetAvailable());
}
how can i implement that ?
thanks
As I understand you want user to see progressbar while your check connection task executes in background. checkInternetAvailable would be your background operation and I wouldn't suggest showing messages directly form it. Instead return a custom struct :
public struct ConnectionCheckResult
{
public bool Success;
public string Message;
}
And this will be your button click event handler :
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = true;
//add code here to be executed on UI thread before connection check
Task.Run(new Action(() =>
{
//Task.Run this code on the thread pool instead of your UI thread. So your code is checking connection while progress bar is still rendering
ConnectionCheckResult res = new checkInternet().checkInternetAvailable();
this.Invoke(new Action(() =>
{
//this.Invoke executes following delegate on UI thread. All UI changes - like progressBar1.Visible = false; need to be made in UI thread.
//add code here to be executed on the UI thread after connection check.
progressBar1.Visible = false;
if (!string.IsNullOrEmpty(res.Message))
MessageBox.Show(res.Message);
}));
}));
//add code to be executed on UI thread at the same time as connection check
}
I know multi-threading is difficult to wrap your head around at first, here's good tutorial with code samples.
Also when your progressbar style is Marquee you don't need to call PerformStep. It will just roll by itself.
EDIT: You should also modify checkInternetAvailable() like so :
public ConnectionCheckResult checkInternetAvailable()
{
int Desc;
ConnectionCheckResult result = new ConnectionCheckResult();
if (InternetGetConnectedState(out Desc, 0) == true)
{
try
{
dbConnection StartConn = new dbConnection();
SqlConnection MyConnetion = StartConn.GetConnection();
MyConnetion.Open();
if (MyConnetion.State == ConnectionState.Open)
{
result.Success = true;
}
MyConnetion.Close();
}
catch (Exception)
{
result.Success = false;
result.Message = "The database connection does not available, May be because of this reasons: \n\n1- there is a new version of the program available. \n2- database has some maintenance. \n\n Please check later :)";
}
}
else
{
result.Success = false;
result.Message = "No internet connection available , Please check later :) \nThanks.";
}
return result;
}

Pausing a new BackGroundWorker until previous completes

I am struggling with threading.
The problem is when I am iterating trough foreach loop.
When setting this.Document, the application performs login, that is triggered with an event and takes few seconds to complete. In the worker_RunWorkerCompleted method I need to perform some actions that depend on current login information.
The problem is that before I can perform this action for the first file, the this.Document already changes making the application perform another login. This way I can never actually perform my actions.
My question is: How can I pause the next thread until previous thread has completed.
Is there any other solution to my problem?
I tried with AutoResetEvent but I got no luck. I set waitOne() just after the RunWorkerAsync call and .Set() in the RunWorkerCompleted. The code never gets to RunWorkerCompleted...
Here is the code:
public void Start(object obj)
{
try
{
foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
{
Worker = new BackgroundWorker();
Worker.DoWork += new DoWorkEventHandler(worker_DoWork);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
using (Stream stream = pair.Value)
{
primaryDocument = new Document(stream);
DataHolderClass dataHolder = new DataHolderClass();
dataHolder.FileName = pair.Key;
dataHolder.Doc = secondaryDocument;
//background thread call
Worker.RunWorkerAsync(dataHolder);
}
}
}
catch (Exception ex)
{
// exception logic
}
finally
{
// complete logic
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
DataHolderClass dataHolder = ((DataHolderClass)e.Argument);
// setting this attribute triggers execution of login event
this.Document = dataHolder.Doc;
e.Result = (dataHolder);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// here I need to perform some actions that are depending on the current login
DataHolderClass dataHolder = ((DataHolderClass)e.Result);
this.eventAggregator.GetEvent<ActionEvent>().Publish(new Message(EMessageType.Info) { Title = dataHolder.FileName });
}
no9,
Try the following:
System.Threading.ManualResetEvent _busy = new System.Threading.ManualResetEvent(false);
void ResumeWorker()
{
// Start the worker if it isn't running
if (!backgroundWorker1.IsBusy) backgroundWorker1.RunWorkerAsync(dataHolder);
// Unblock the worker
_busy.Set();
}
void PauseWorker()
{
// Block the worker
_busy.Reset();
}
void CancelWorker()
{
if (backgroundWorker1.IsBusy) {
// Set CancellationPending property to true
backgroundWorker1.CancelAsync();
// Unblock worker so it can see that
_busy.Set();
}
}
then in your code run the method.
Let me know if it works :)
class SimpleWaitPulse
{
static readonly object _locker = new object();
static bool _go;
static void Main()
{ // The new thread will block
new Thread (Work).Start(); // because _go==false.
Console.ReadLine(); // Wait for user to hit Enter
lock (_locker) // Let's now wake up the thread by
{ // setting _go=true and pulsing.
_go = true;
Monitor.Pulse (_locker);
}
}
static void Work()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker); // Lock is released while we’re waiting
Console.WriteLine ("Woken!!!");
}
}
Can you use pulse ?
Taken from : Threading in C# from albahari.com
Well, the design is terrible... but if you need to stick to it, you can set wait handles in a previous worker and wait for it in next. This is the minimal fix, still quite an abomination:
public void Start(object obj)
{
try
{
BackgroundWorker previousWorker = null;
DataHolderClass previousWorkerParams = null;
foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
{
// signal event on previous worker RunWorkerCompleted event
AutoResetEvent waitUntilCompleted = null;
if (previousWorker != null)
{
waitUntilCompleted = new AutoResetEvent(false);
previousWorker.RunWorkerCompleted += (o, e) => waitUntilCompleted.Set();
// start the previous worker
previousWorker.RunWorkerAsync(previousWorkerParams);
}
Worker = new BackgroundWorker();
Worker.DoWork += (o, e) =>
{
// wait for the handle, if there is anything to wait for
if (waitUntilCompleted != null)
{
waitUntilCompleted.WaitOne();
waitUntilCompleted.Dispose();
}
worker_DoWork(o, e);
};
using (Stream stream = pair.Value)
{
primaryDocument = new Document(stream);
DataHolderClass dataHolder = new DataHolderClass();
dataHolder.FileName = pair.Key;
dataHolder.Doc = secondaryDocument;
// defer running this worker; we don't want it to finish
// before adding additional completed handler
previousWorkerParams = dataHolder;
}
previousWorker = Worker;
}
if (previousWorker != null)
{
previousWorker.RunWorkerAsync(previousWorkerParams);
}
}
catch (Exception ex)
{
// exception logic
}
finally
{
// complete logic
}
}

Categories