Timer control not working in ASP.net - c#

I have a situation like this.
TestClass - a class defined in UI Layer
test - a class level variable
Submit button calls a function DoSomeThing()..It performs some operation in Busines Layer keeps on updating the Status Property of the class
Timercontrol getting the status (from same variable test)to display in UI
Submit button runs by a thread (say thread 1 ) and starts the operation. Is is this thread which updates the status property from the BL
Timer control creates a new thread each time to run the TimerEvent (Say thread 2 , 3 etc).
Issue here is that test.Status property , which is updated by thread1 is not accessible by other thread.. It is always null , even though the property has been updated by thread 1..
What is the solution for this ?
Thanks in advance
public class TestClass //---->#1
{
private test = new Test() ; //---->#2
protected void SubmitButon_Click(object sender, EventArgs e)
{
// This is performed by Thread1
test.DoSomeThing() //------>#3
}
protected void UpdateTimer_Tick(object sender, EventArgs e)
{
// Timer controls sends out a new thread each time
Label1.Text = test.Status; //------>#4
}
}

here's sample to use delegate and update UI ements from different thread
delegate string CallFunctionDelegate(string arg1, string arg2);
private void btnStart_Click(object sender, EventArgs e)
{
CallFunctionDelegate delegRunApps = new CallFunctionDelegate(DoSomeThingBig);
AsyncCallback CallBackAfterAsynOperation = new AsyncCallback(AfterDoingSomethingBig);
delegRunApps.BeginInvoke("", "", CallBackAfterAsynOperation, null);
}
private string DoSomeThingBig(string arg1, string arg2)
{
#region Implemetation of time consuming function
//Implemetation of time consuming function
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000);
if (btnStart.InvokeRequired)
{
btnStart.Invoke((new MethodInvoker(delegate { btnStart.Text = i.ToString(); })));
}
else
{
btnStart.Text = i.ToString();
}
}
#endregion
return arg1.Replace("freetime", arg2);
}
private void AfterDoingSomethingBig(IAsyncResult result)
{
MessageBox.Show("Finaly Done!! ;) ");
btnStart.Invoke((new MethodInvoker(delegate { btnStart.Text = "Start"; })));
}

Issue happens because a new instance is created by the timerthread eachtime after as Line #2 is executed..Hence test.Status is always null.. That was the reason for the issue

Related

C# - WPF - Updating the UI from another class on another Thread

I have looked around the internet and found a nice solution which I am incorporating into my code below however it doesn't quite do exactly what I want, it works when just calling an update but I want to run a method in another class then let that method call the method that will report back to the UI and just pass some information so this mock up is just changing the button content before the operation is ran.
Using a Dispatcher I can get a UI control to update however I don't just wish to do that I want to perform some functions then have the UI Update.
So there maybe some theory I am not getting, I know the Invoke is a synchronous operation and breaking through the code it does run but the UI doesn't update.
MainWindow
Has a single button with content "CLICK ME"
Code Behind
public partial class MainWindow : Window
{
public static Button windowButton;
public MainWindow()
{
InitializeComponent();
windowButton = btnStart;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
// Run a Process in another class on a different Thread
ProcessClass processClass = new ProcessClass();
Task processTask = new Task(() =>
{
processClass.DoSomething();
});
processTask.Start();
}
}
}
ProcessClass
class ProcessClass:MainWindow
{
public static void UpdateUI()
{
App.Current.Dispatcher.Invoke(delegate
{
windowButton.Content = "CHANGED CONTENT";
});
}
public void DoSomething()
{
UpdateUI();
int counter = 0;
for(int i = 1; i < 100; i++)
{
counter += i;
Thread.Sleep(100);
}
MessageBox.Show($"Task Completed, answer is {counter}");
}
}
Assuming that ProcessClass is your own code that you can update, change the signiture of DoDomething() to
public async Task DoSomething(IProgress<string> progress)
{
progress.Report("Begin DoSomething()");
var counter = 0;
for(var i = 1; i < 100; i++)
{
counter += i;
await Task.Delay(100).ConfigureAwait(false);
progress.Report($"DoSomething() - i = {i}");
}
progress.Report($"DoSomething() Completed, answer is {counter}");
}
Now your button click handler can be written
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
// usually you would update some other control such as a TextBlock
// for the feedback, rather than the button content
var progress = new Progress<string>(s => btnStart.Content = s);
ProcessClass processClass = new ProcessClass();
await processClass.DoSomething(progress).ConfigureAwait(false);
}

Thread safe C# not working

when I run the following code that runs fine, but not as expected, it is assumed to run on a secure thread, but all components are frozen until it finishes running the thread, it is not supposed to run on a new thread so you can use other controls?
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
public class MyFormControl : Form
{
public delegate void AddListItem();
public AddListItem myDelegate;
private Button myButton;
private Thread myThread;
private ListBox myListBox;
public MyFormControl()
{
myButton = new Button();
myListBox = new ListBox();
myButton.Location = new Point(72, 160);
myButton.Size = new Size(152, 32);
myButton.TabIndex = 1;
myButton.Text = "Add items in list box";
myButton.Click += new EventHandler(Button_Click);
myListBox.Location = new Point(48, 32);
myListBox.Name = "myListBox";
myListBox.Size = new Size(200, 95);
myListBox.TabIndex = 2;
ClientSize = new Size(292, 273);
Controls.AddRange(new Control[] {myListBox,myButton});
Text = " 'Control_Invoke' example";
myDelegate = new AddListItem(AddListItemMethod);
}
static void Main()
{
MyFormControl myForm = new MyFormControl();
myForm.ShowDialog();
}
public void AddListItemMethod()
{
String myItem;
for(int i=1;i<6;i++)
{
myItem = "MyListItem" + i.ToString();
myListBox.Items.Add(myItem);
myListBox.Update();
Thread.Sleep(300);
}
}
private void Button_Click(object sender, EventArgs e)
{
myThread = new Thread(new ThreadStart(ThreadFunction));
myThread.Start();
}
private void ThreadFunction()
{
MyThreadClass myThreadClassObject = new MyThreadClass(this);
myThreadClassObject.Run();
}
}
// The following code assumes a 'ListBox' and a 'Button' control are added to a form,
// containing a delegate which encapsulates a method that adds items to the listbox.
public class MyThreadClass
{
MyFormControl myFormControl1;
public MyThreadClass(MyFormControl myForm)
{
myFormControl1 = myForm;
}
public void Run()
{
// Execute the specified delegate on the thread that owns
// 'myFormControl1' control's underlying window handle.
myFormControl1.Invoke(myFormControl1.myDelegate);
}
}
it is assumed to run on a secure thread, but all components are frozen
until it finishes running the thread
When you are invoking some delegate on control, the delegate will run on UI thread. I.e. this code will run on UI thread:
public void AddListItemMethod()
{
String myItem;
for(int i=1;i<6;i++)
{
myItem = "MyListItem" + i.ToString();
myListBox.Items.Add(myItem);
myListBox.Update();
Thread.Sleep(300); // freeze UI thread
}
}
it is not supposed to run on a new thread so you can use other
controls?
You cannot use controls from non-UI threads.
Purpose of using background threads is a long-running operations which are not related to UI. E.g. you can read some file from disk, query api, or you can run some long-running calculation (n-th fibonacci number). If you'll run these kind of things on UI thread, then your application will freeze. So you should run such operations on non-UI thread and return to UI after you have finished long-running operation (though you can notify user about progress of long-running operation).
If you want to periodically do something with UI then consider using System.Windows.Forms.Timer component. Set timer interval to 300 and add Tick event handler:
private void Button_Click(object sender, EventArgs e)
{
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
myListBox.Items.Add($"MyListItem{myListBox.Items.Count + 1}");
}
The problem is that Invoke runs the delegate on the UI thread, so you're just creating a thread that just tells the UI thread to do all the work. Instead you can use async and await along with Task.Delay to simplify your code.
private async void Button_Click(object sender, EventArgs e)
{
String myItem;
for(int i=1;i<6;i++)
{
myItem = "MyListItem" + i.ToString();
myListBox.Items.Add(myItem);
myListBox.Update();
await Task.Delay(300);
}
}
That will run the code on the UI, but now the await Task.Delay(300) will not block the UI thread allowing it to handle other UI events and stopping the freezing problem.

Using Auto reset event is not working as expected

I am quite new to c# programming, I am trying to achieve the following result but failing to do so.
What I expect -:
On a click event of a button, I want to open an applciation via its API, run analysis and then exit the application. While running the application I have a progress bar on the form which should keep going from 0 - 100 till the RunAnalysis() method called through the API gets executed, when it gets executed the progress bar should show as 100% and the application called through should exit
What is happening -:
The RunanAlysis() is being executed and the application exits, the click event of the button gets executed and then the progress bar moves from 0 - 100 which should not happen
What is my attempt
namespace trialapp
{
public partial class Form1 : Form
{
AutoResetEvent obj = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
}
ETABS2015.cSapModel SapModel;
System.Reflection.Assembly ETABSAssembly;
ETABS2015.cOAPI ETABSObject;
int result = -1;
delegate int MyDelegate();
MyDelegate pointer = null;
private void button1_Click(object sender, EventArgs e)
{
//Use ret to check return values of OAPI calls
int ret;
//Dynamically load ETABS.exe assembly from the program installation folder
string pathToETABS = System.IO.Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES"), "Computers and Structures", "ETABS 2013", "ETABS.exe");
ETABSAssembly = System.Reflection.Assembly.LoadFrom(pathToETABS);
//Create an instance of ETABSObject and get a reference to cOAPI interface
ETABSObject = (ETABS2015.cOAPI)ETABSAssembly.CreateInstance("CSI.ETABS.API.ETABSObject");
//Start ETABS application
ret = ETABSObject.ApplicationStart();
//Get a reference to cSapModel to access all OAPI classes and functions
SapModel = ETABSObject.SapModel;
//Initialize model
ret = SapModel.InitializeNewModel();
//Create steel deck template model
ret = SapModel.File.NewSteelDeck(4, 12, 12, 4, 4, 24, 24);
//Save model
System.IO.Directory.CreateDirectory("C:\\ETABSAPI");
ret = SapModel.File.Save("C:\\ETABSAPI\\example2.edb");
//Run analysis
backgroundWorker1.RunWorkerAsync();
// ret = SapModel.Analyze.RunAnalysis();
obj.WaitOne();
//Close ETABS
ret = ETABSObject.ApplicationExit(false);
//Clean up variables
SapModel = null;
ETABSObject = null;
//Check ret value
if (ret == 0)
{
MessageBox.Show("API script completed succesfully.");
}
else
{
MessageBox.Show("API script FAILED to complete.");
}
}
public void AfterRunAnalysisComplete(IAsyncResult resultHolder)
{
result = pointer.EndInvoke(resultHolder);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
pointer = new MyDelegate(SapModel.Analyze.RunAnalysis);
IAsyncResult flag = pointer.BeginInvoke(new AsyncCallback(AfterRunAnalysisComplete), null);
while (!flag.IsCompleted)
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
backgroundWorker1.ReportProgress(i);
if (i == 100)
{
i = 0;
}
if (flag.IsCompleted)
{
break;
}
}
}
backgroundWorker1.ReportProgress(100);
//obj.Set();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
}
Can any one tell me as to where exactly am I going wrong?
Edit -:
I tried not using WaitOne() and putting the code which followsbackgroundWorker1.RunWorkerAsync(); in the backgroundWorker1_DoWork method, but that is not I want to do as the extent of main project is too much and this will not make sense with the design of classes.
The problem, as I see it, is when you kick off
ret = ETABSObject.ApplicationStart();
it's going to start it in a new thread, which your program isn't going to have access to. I would recommend starting the application in a TaskFactory, then you can use a while to check if the task is still running and update your progress bar.
Maybe something like this:
var etabApp = Task.Factory.Startnew(() => { ETABSObject.ApplicationStart()});
while(etabApp.Status == TaskStatus.Running)
{
//Do something to check the percent complete and update the progress bar
}

How to stop a for loop using a button?

I have a loop that I would like to stop using a button.
Edited for better understanding:
I do realize that you cannot stop a button while a loop was running since it will not work as long as that current UI is running. What I'm really asking for is the most efficient way of creating a thread or using BGWorker to stop this. I have seen some methods, but most of them are for Java and not C#.
What I would like to do is:
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums; i++)
{
doSomething();
}
}
private void stop_Click(object sender, EventArgs e)
{
stops start_Click()
}
You can't do that. For starters, the for loop is running synchronously on the UI thread, which means you won't even be able to click the "Stop" button.
Hence, you need to move the operations of the for loop onto another thread, which means you likely won't be using a for loop at all. You need to think about how the code inside actually needs to be executed, then based on how you are doing the processing, you can implement the "Stop" button.
A very simple way to do this would be to just:
new Thread(() =>
{
int i = 0;
while (!stop && i < num)
{
doSomething();
i++;
}
}).Start();
And set stop to stop the processing loop. In a more realistic scenario, you could queue up functions that you want to process, then stop dequeuing via a similar method. Unfortunately, its hard to reccommend a setup without knowing more details.
Any solution based on your code will also have the problem of the current doSomething() completing execution (which could take a while). Again, without more info, its hard to say what the best approach to fixing that is.
To keep your UI responsive to be able to cancel the running operation you can use a backgrounworker.
The backgroundworker does the work in an other thread while keeping your UI responsive:
private readonly BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
_backgroundWorker.DoWork += backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
Disposed += Form1_Disposed;
}
private void Form1_Disposed(object sender, EventArgs e)
{
_backgroundWorker.Dispose();
}
private void StartLoop()
{
if ( !_backgroundWorker.IsBusy )
{
_backgroundWorker.RunWorkerAsync();
}
}
private void StopLoop()
{
_backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork( object sender , DoWorkEventArgs e )
{
var backgroundWorker = ( BackgroundWorker ) sender;
for ( var i = 0; i < 100; i++ )
{
if ( backgroundWorker.CancellationPending )
{
e.Cancel = true;
return;
}
// Do Work
}
}
private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
{
if ( e.Cancelled )
{
// handle cancellation
}
if ( e.Error != null )
{
// handle error
}
// completed without cancellation or exception
}
IMHO, it's likely the best approach here is to convert your work to an asynchronous operation and then use the async/await idiom for the loop. E.g.:
private bool _stopLoop;
private async void start_Click(object sender, EventArgs e)
{
_stopLoop = false;
for(int i = 0; i < nums && !_stopLoop; i++)
{
await Task.Run(() => doSomething());
}
}
private void stop_Click(object sender, EventArgs e)
{
_stopLoop = true;
}
This allows the loop itself to execute in the UI thread where the _stopLoop variable is being managed, but without actually blocking the UI thread (which among other things would prevent the "Stop" button from being clicked).
Unfortunately, you didn't provide details about how doSomething() works. It's possible there's a good way to convert that whole method to be an async method, but I can't comment on that without the actual code.
Note that this approach will only interrupt the loop at a point in between each operation. If you want to be able to interrupt the doSomthing() operation itself, you'll have to provide a mechanism for that. One likely approach would be to use CancellationSource and CancellationToken, which provides a convenient way to express cancellation semantics.
Try using an async/await approach. It's quite easy!
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
private CancellationTokenSource _tokenSource;
private async void start_Click(object sender, EventArgs e)
{
if (_tokenSource != null)
return;
_tokenSource = new CancellationTokenSource();
var ct = _tokenSource.Token;
await Task.Factory.StartNew(() =>
{
for (; ; )
{
if (ct.IsCancellationRequested)
break;
doSomething();
}
}, ct);
_tokenSource = null;
}
private int _labelCounter;
private void doSomething()
{
// do something
Invoke((Action)(() =>
{
myLabel.Text = (++_labelCounter).ToString();
}));
}
private void stop_Click(object sender, EventArgs e)
{
if (_tokenSource == null)
return;
_tokenSource.Cancel();
}
}
try this :
bool stop=false;
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums&& !bool; i++)
{
doSomething();
}
}
and in the click event
set
stop=true;

Cant understand: using lock () but never executing code

I'm learning about threads in C#, and i get this behavior that i cant understand.
The code simulates I/O operations, like files or serial port, where only one thread can access it at time, and it blocks until finishes.
Four threads are started. Each performs just a count. It works ok, i can see on the form the counts growing. But there is a button to count from the form thread. When i push it, the main thread freezes. The debugger shows that the others threads keep counting, one by one, but the form thread never gets access to the resource.
1) Why the lock(tty) from the form thread never gets access to it, when the others threads has no problem ?
2) Is there a better way to do this type of synchronization ?
Sorry about the big code:
public class MegaAPI
{
public int SomeStupidBlockingFunction(int c)
{
Thread.Sleep(800);
return ++c;
}
}
class UIThread
{
public delegate void dlComandoMaquina();
public class T0_SyncEvents
{
private EventWaitHandle _EventFechar; // Exit thread event
public T0_SyncEvents()
{
_EventFechar = new ManualResetEvent(false);
}
public EventWaitHandle EventFecharThread // Exit thread event
{
get { return _EventFechar; }
}
}
public class T0_Thread
{
private T0_SyncEvents _syncEvents;
private int _msTimeOut;
private dlComandoMaquina _ComandoMaquina;
public T0_Thread(T0_SyncEvents e, dlComandoMaquina ComandoMaquina, int msTimeOut)
{
_syncEvents = e;
_msTimeOut = msTimeOut;
_ComandoMaquina = ComandoMaquina;
}
public void VaiRodar() // thread running code
{
while (!_syncEvents.EventFecharThread.WaitOne(_msTimeOut, false))
{
_ComandoMaquina();
}
}
}
}
public partial class Form1 : Form
{
MegaAPI tty;
UIThread.T0_Thread thr1;
UIThread.T0_SyncEvents thrE1;
Thread Thread1;
int ACount1 = 0;
void UIUpdate1()
{
lock (tty)
{
ACount1 = tty.SomeStupidBlockingFunction(ACount1);
}
this.BeginInvoke((Action)delegate { txtAuto1.Text = ACount1.ToString(); });
}
UIThread.T0_Thread thr2;
UIThread.T0_SyncEvents thrE2;
Thread Thread2;
int ACount2 = 0;
void UIUpdate2()
{
lock (tty)
{
ACount2 = tty.SomeStupidBlockingFunction(ACount2);
}
this.BeginInvoke((Action)delegate { txtAuto2.Text = ACount2.ToString(); });
}
UIThread.T0_Thread thr3;
UIThread.T0_SyncEvents thrE3;
Thread Thread3;
int ACount3 = 0;
void UIUpdate3()
{
lock (tty)
{
ACount3 = tty.SomeStupidBlockingFunction(ACount3);
}
this.BeginInvoke((Action)delegate { txtAuto3.Text = ACount3.ToString(); });
}
UIThread.T0_Thread thr4;
UIThread.T0_SyncEvents thrE4;
Thread Thread4;
int ACount4 = 0;
void UIUpdate4()
{
lock (tty)
{
ACount4 = tty.SomeStupidBlockingFunction(ACount4);
}
this.BeginInvoke((Action)delegate { txtAuto4.Text = ACount4.ToString(); });
}
public Form1()
{
InitializeComponent();
tty = new MegaAPI();
thrE1 = new UIThread.T0_SyncEvents();
thr1 = new UIThread.T0_Thread(thrE1, UIUpdate1, 500);
Thread1 = new Thread(thr1.VaiRodar);
Thread1.Start();
thrE2 = new UIThread.T0_SyncEvents();
thr2 = new UIThread.T0_Thread(thrE2, UIUpdate2, 500);
Thread2 = new Thread(thr2.VaiRodar);
Thread2.Start();
thrE3 = new UIThread.T0_SyncEvents();
thr3 = new UIThread.T0_Thread(thrE3, UIUpdate3, 500);
Thread3 = new Thread(thr3.VaiRodar);
Thread3.Start();
thrE4 = new UIThread.T0_SyncEvents();
thr4 = new UIThread.T0_Thread(thrE4, UIUpdate4, 500);
Thread4 = new Thread(thr4.VaiRodar);
Thread4.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
thrE1.EventFecharThread.Set();
thrE2.EventFecharThread.Set();
thrE3.EventFecharThread.Set();
thrE4.EventFecharThread.Set();
Thread1.Join();
Thread2.Join();
Thread3.Join();
Thread4.Join();
}
int Mcount = 0;
private void btManual_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
lock (tty) // locks here ! Never runs inside! But the other threads keep counting..
{
Mcount = tty.SomeStupidBlockingFunction(Mcount);
txtManual.Text = Mcount.ToString();
}
Cursor.Current = Cursors.Default;
}
}
I suspect you are hitting something with the Windows message loop and threading in WinForms. I don't know what that is, but here are a few pointers:
You can run the button's task in a backgroundWorker to keep the work off the UI thread. That solves the lock problem. Drag a BackgroundWorker from the toolbox and drop it on your Form in the designer, and hook up the event, i.e.:
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
then switch your code in btManual_Click to call the background worker like this:
backgroundWorker1.RunWorkerAsync();
and then:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Mcount = tty.SomeStupidBlockingFunction(Mcount);
this.BeginInvoke((Action)delegate { txtManual.Text = Mcount.ToString(); });
}
I've left out the lock (tty) because I would rather see only one of these statements inside the function, rather than five of them outside. And instead of locking on tty, I would create a private variable like this:
public class MegaAPI
{
private object sync = new object();
public int SomeStupidBlockingFunction(int c)
{
lock (this.sync)
{
Thread.Sleep(800);
return ++c;
}
}
}
Everywhere else is then simplified, for example:
void UIUpdate1()
{
ACount1 = tty.SomeStupidBlockingFunction(ACount1);
this.BeginInvoke((Action)delegate { txtAuto1.Text = ACount1.ToString(); });
}
And since you can't run the background worker while it's still processing, here is a quick-and-dirty solution: disable the button while it's working:
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
and then:
private void btManual_Click(object sender, EventArgs e)
{
this.btManual.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
and:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.btManual.Enabled = true;
}
So I recommend:
Keep a single lock () statement
inside the function needing the
synchronization
Keep the lock object private
Run the work on a background worker
Mutexes do not provide fairness by default. They just guarantee that your process as a whole will make forward progress. It is the implementation's job to pick the best thread to get the mutex based on characteristics of the scheduler and so on. It is the coder's job to make sure that the thread that gets the mutex does whatever work the program needs done.
If it's a problem for you if the "wrong thread" gets the mutex, you are doing it wrong. Mutexes are for cases where there is no "wrong thread". If you need fairness or predictable scheduling, you need to use a locking primitive that provides it or use thread priorities.
Mutexes tend to act in strange ways when threads that hold them aren't CPU-limited. Your threads acquire the mutex and then deschedule themselves. This will lead to degenerate scheduling behavior just like the behavior you're seeing. (They won't break their guarantees, of course, but they will act much less like a theoretically perfect mutex that also provided things like fairness.)

Categories