I want all the methods of my class to be allowed to be called only from the same thread that the object was created on. The same way as Windows Forms controls do not allow their method/properties to be accessed on any other thread except the UI thread and they throw an exception if you try to do that. What are the winforms' controls doing in order to enforce such behavior? Is there anything that I can use to annotate the methods (like some special attribute) or I have to handle those situations "by hand"?
EDIT 1: Here is the code of the Handle property getter of the WinForms' Control class
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
This is the code of InvokeRequired of the WinForms' Control class
public bool InvokeRequired {
get {
using (new MultithreadSafeCallScope())
{
HandleRef hwnd;
if (IsHandleCreated) {
hwnd = new HandleRef(this, Handle);
}
else {
Control marshalingControl = FindMarshalingControl();
if (!marshalingControl.IsHandleCreated) {
return false;
}
hwnd = new HandleRef(marshalingControl, marshalingControl.Handle);
}
int pid;
int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(hwnd, out pid);
int currentThread = SafeNativeMethods.GetCurrentThreadId();
return(hwndThread != currentThread);
}
}
}
Looking at this code I can see one solution to the problem will be in the constructor of the object we store the current thread id in private field and then on every subsequent call we check if the current thread id differs from the id we have stored, throwing InvalidOperationException
if that's the case. Do you find any problems with the aforementioned solution or do you know a better approach?
static class Program
{
static void Main()
{
MySafeClass c = new MySafeClass();
Task.Factory.StartNew(() => c.Operation1(), TaskCreationOptions.LongRunning); // This should throw an exception
}
}
class MySafeClass
{
private readonly int creationThreadId;
public MySafeClass()
{
creationThreadId = Thread.CurrentThread.ManagedThreadId;
}
private void CheckCrossThreadOperation()
{
if (this.creationThreadId != Thread.CurrentThread.ManagedThreadId)
{
throw new InvalidOperationException("Cross thread operatoin is not allowed");
}
}
public void Operation1()
{
CheckCrossThreadOperation();
// ...
}
public void Operation2()
{
CheckCrossThreadOperation();
// ...
}
}