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) { }
}
Related
Good day.
I'm having a problem exiting a task with the cancellation token.
My program freezes when I get to the token2.ThrowIfCancellationRequested();.
Following it with the breakpoints is shows that the token2 is cancelled, but the program doesn't revert back to the previous sub routine where I try and catch
try
{
Task.Run(() => SendData_DoWork(_tokenSource3));
}
catch (OperationCanceledException ex)
{
SetText("Communivation error with device");
SetText("");
}
finally
{
token.Dispose();
}
}//comms routine
//send Meter Address to communicate to meter
private void SendData_DoWork(CancellationTokenSource token)
{
var token2 = token.Token;
var _tokenSource4 = new CancellationTokenSource();
try
{
timer.Interval = 10000;
timer.Start();
timer.Elapsed += OnTimerElapsed;
NetworkStream stream = client.GetStream();
SerialConverter serialConverter = new SerialConverter();
Thread.Sleep(1000);
string newtext = null;
newtext = $"/?{address}!\r\n";
SetText("TX: " + newtext);
byte[] newData = stringSend(newtext);
stream.Write(newData, 0, newData.Length);
Thread.Sleep(50);
byte[] message = new byte[23];
int byteRead;
while (true)
{
byteRead = 0;
try
{
byteRead = stream.Read(message, 0, 23);
if (message[0] == (char)0x15)
{
token.Cancel();
}
}
catch
{
token.Cancel();
}
if ((byteRead == 0))
{
token.Cancel();
}
timer.Stop();
timer.Dispose();
ASCIIEncoding encoder = new ASCIIEncoding();
string newresponse = encoder.GetString(serialConverter.convertFromSerial(message));
SetText("RX: " + newresponse);
if (newresponse[0].ToString() == SOH)
{
token.Cancel();
}
if (newresponse != null)
{
/* NEXT SUB ROUTINE*/
}
else { break; }
}//while looop
}//try
catch (Exception ex)
{
token.Cancel();
}
if (token2.IsCancellationRequested)
{
timer.Stop();
timer.Dispose();
token2.ThrowIfCancellationRequested();
}
}//sendData subroutine
You are launching a Task, and ignoring the result; the only time Task.Run would throw is if the task-method is invalid, or enqueuing the operation itself failed. If you want to know how SendData_DoWork ended, you'll need to actually check the result of the task, by capturing the result of Task.Run and awaiting it (preferably asynchronously, although if we're talking async, SendData_DoWork should probably also be async and return a Task).
Your catch/finally will probably be exited long before SendData_DoWork even starts - again: Task.Run just takes the time required to validate and enqueue the operation; not wait for it to happen.
I think you have missunderstood how cancellation tokens are supposed to work. Your work method should take a CancellationToken, not a CancellationTokenSource. And it should call ThrowIfCancellationRequested inside the loop, not after. I would suspect that you would get some issues with multiple cancel calls to the same cancellation token.
Typically you would use a pattern something like like this:
public void MyCancelButtonHandler(...) => cts.Cancel();
public async void MyButtonHandler(...){
try{
cts = new CancellationTokenSource(); // update shared field
await Task.Run(() => MyBackgroundWork(cts.Token));
}
catch(OperationCancelledException){} // Ignore
catch(Exception){} // handle other exceptions
}
private void MyBackgroundWork(CancellationToken cancel){
while(...){
cancel.ThrowIfCancellationRequested();
// Do actual work
}
}
So in my particular case it seems like changing the sub-routines from private async void ... to private async Task fixes the particular issue that I'm having.
I implemented Task synchronization using Monitor in C#.
However, I have read Monitor should not be used in asynchronous operation.
In the below code, how do I implement Monitor methods Wait and PulseAll with a construct that works with Task (asynchronous operations).
I have read that SemaphoreSlim.WaitAsync and Release methods can help.
But how do they fit in the below sample where multiple tasks need to wait on a lock object, and releasing the lock wakes up all waiting tasks ?
private bool m_condition = false;
private readonly Object m_lock = new Object();
private async Task<bool> SyncInteralWithPoolingAsync(
SyncDatabase db,
List<EntryUpdateInfo> updateList)
{
List<Task> activeTasks = new List<Task>();
int addedTasks = 0;
int removedTasks = 0;
foreach (EntryUpdateInfo entryUpdateInfo in updateList)
{
Monitor.Enter(m_lock);
//If 5 tasks are waiting in ProcessEntryAsync method
if(m_count >= 5)
{
//Do some batch processing to obtian values to set for adapterEntry.AdapterEntryId in ProcessEntryAsync
//.......
//.......
m_condition = true;
Monitor.PulseAll(m_lock); // Wakes all waiters AFTER lock is released
}
Monitor.Exit(m_lock);
removedTasks += activeTasks.RemoveAll(t => t.IsCompleted);
Task processingTask = Task.Run(
async () =>
{
await this.ProcessEntryAsync(
entryUpdateInfo,
db)
.ContinueWith(this.ProcessEntryCompleteAsync)
.ConfigureAwait(false);
});
activeTasks.Add(processingTask);
addedTasks++;
}
}
private async Task<bool> ProcessEntryAsync(SyncDatabase db, EntryUpdateInfo entryUpdateInfo)
{
SyncEntryAdapterData adapterEntry =
updateInfo.Entry.AdapterEntries.FirstOrDefault(e => e.AdapterId == this.Config.Id);
if (adapterEntry == null)
{
adapterEntry = new SyncEntryAdapterData()
{
SyncEntry = updateInfo.Entry,
AdapterId = this.Config.Id
};
updateInfo.Entry.AdapterEntries.Add(adapterEntry);
}
m_condition = false;
Monitor.Enter(m_lock);
while (!m_condition)
{
m_count++;
Monitor.Wait(m_lock);
}
m_count--;
adapterEntry.AdapterEntryId = .... //Set Value obtained form batch processing
Monitor.Exit(m_lock);
}
private void ProcessEntryCompleteAsync(Task<bool> task, object context)
{
EntryProcessingContext ctx = (EntryProcessingContext)context;
try
{
string message;
if (task.IsCanceled)
{
Logger.Warning("Processing was cancelled");
message = "The change was cancelled during processing";
}
else if (task.Exception != null)
{
Exception ex = task.Exception;
Logger.Warning("Processing failed with {0}: {1}", ex.GetType().FullName, ex.Message);
message = "An error occurred while synchronzing the changed.";
}
else
{
message = "The change was successfully synchronized";
if (task.Result)
{
//Processing
//...
//...
}
}
}
catch (Exception e)
{
Logger.Info(
"Caught an exception while completing entry processing. " + e);
}
finally
{
}
}
Thanks
So I am talking to something externally and I have just sent it a message, I will expect an almost immediate response but I will wait for a second in case there is a delay. A separate thread is monitoring for an input and will set a flag "newdataflag" when it has received this data. All I am trying to do below is wait in a while loop until this flag is set or 1 second elapses.
private bool WaitrxData()
{
System.Windows.Threading.DispatcherTimer waitrxtimer = new System.Windows.Threading.DispatcherTimer();
waitrxtimer.Tick += waitrxtimer_Tick;
waitrxtimer.Interval = TimeSpan.FromMilliseconds(10);
waitrxtimer.IsEnabled = true;
waitrxtimer.Start();
statusText.Text = "Waiting For Response";
//wait for new data
while (!newdataflag)
{
if (waitrxcounter > 100)
{
statusText.Text = "No Response";
break;
}
}
waitrxtimer.Stop();
if (waitrxcounter > 100)
{
return false;
}
else
{
newdataflag = false;
return true;
}
}
private void waitrxtimer_Tick(object sender, EventArgs e)
{
waitrxcounter++;
}
This code works if the response is so immediate the while loop is skipped but if not, the code will not execute the timer and just get stuck in the while loop and crash. I think this is because the timer is not creating a new thread to tick like I thought it would?
Maybe I am doing the wrong thing?
Cheers
Instead of busy waiting in a loop you could await Task.Delay() with a CancellationToken:
private CancellationTokenSource cts = new CancellationTokenSource();
...
private async Task<bool> Wait()
{
try
{
await Task.Delay(1000, cts.Token);
return true;
}
catch
{
return false;
}
}
// cancel Wait() from another method
cts.Cancel();
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.
I created a thread in C # 4.0 and would like to know how do I check if it is running?
You can use Thread.IsAlive to check to see if a Thread is running.
That being said, if you're using C# 4, it's rarely a good idea to make "threads" manually. You should consider using the TPL and the Task/Task<T> class, as this provides a much cleaner model to attach work to run after the task completes, pull data out of the operation, etc.
I use Mutex to verify this. Sometimes just verify is Thread is alive with Thread.IsAlive is not safe if you are running on Background.
Try this:
private void btnDoSomething()
{
try
{
string nameThread = "testThreadDoSomething";
var newThread = new Thread(delegate() { this.DoSomething(nameThread); });
newThread.IsBackground = true;
newThread.Name = nameThread;
newThread.Start();
//Prevent optimization from setting the field before calling Start
Thread.MemoryBarrier();
}
catch (Exception ex)
{
}
}
public void DoSomething(string threadName)
{
bool ownsMutex;
using (Mutex mutex = new Mutex(true, threadName, out ownsMutex))
{
if (ownsMutex)
{
Thread.Sleep(300000); // 300 seconds
if (Monitor.TryEnter(this, 300))
{
try
{
// Your Source
}
catch (Exception e)
{
string mensagem = "Error : " + e.ToString();
}
finally
{
Monitor.Exit(this);
}
}
//mutex.ReleaseMutex();
}
}
}