1

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();
            // ...
        }
    }
Mihail Shishkov
  • 14,129
  • 7
  • 48
  • 59
  • show us what you've done so far. – Raktim Biswas May 14 '16 at 11:45
  • You can create a UI element in the methods class, then check it dispatcher access within the method. – Wobbles May 14 '16 at 11:46
  • Take a look at [this SO question](http://stackoverflow.com/questions/2367718/automating-the-invokerequired-code-pattern). – Alessandro D'Andria May 14 '16 at 11:52
  • Why do you want this limitation? If it is because you make a control that listens to event you may benefit from being a Control, otherwise I guess you'll have to implement ISynchronizeInvoke "by hand". – Mattias Åslund May 14 '16 at 12:10
  • @MattiasÅslund there is no Controls involved here it is a pure C# object no WinForms at all. My class is not thread safe so that I want to make that apparent to the consumer by throwing an exception if he/she tries to call it from another thread. – Mihail Shishkov May 14 '16 at 12:20
  • 1
    List<> is not threadsafe either, and you don't see ms forcing us to jump through hoops to use it. Don't impede your future self unless you really have to – Mattias Åslund May 14 '16 at 12:23
  • @MattiasÅslund as you can guess I have reasons to ask this question :) The WinForms Control is too Microsoft creation and they enforced this on us because they new it will save a lot of users if they stick to the rule for calling Control's methods on the UI thread. This is a design decision that goes well with the current architecture I have. So I don't see what's the problem here. – Mihail Shishkov May 14 '16 at 12:28
  • This is an atypical way of handling a concurrent environment. Are you familiar with the concept of a mutex? – D3C34C34D May 14 '16 at 12:43

0 Answers0