.netmultithreading.net-9.0

Using BindingOperations.EnableCollectionSynchronization with the new System.Threading.Lock class


The built in static method BindingOperations.EnableCollectionSynchronization requires a general object. .Net 9 has introduced the class System.Threading.Lock that improves performance and is a specific class for performing locks. What is the alternative in this case to use the Lock itself?


Solution

  • You should be able to use the following overload to use the new Lock type:

    BindingOperations.EnableCollectionSynchronization(IEnumerable, object, CollectionSynchronizationCallback)

    Enables a CollectionView object to participate in synchronized access to a collection used on multiple threads by using a mechanism other than a simple lock.

    You could use it via an extension method like so:

    public static class BindingOperationsExtensions
    {
        static CollectionSynchronizationCallback callback = static (collection, context, accessMethod, writeAccess) =>
        {
            var @lock = (Lock)context;
            using (@lock.EnterScope())
            {
                accessMethod();
            }
        };
        
        public static void EnableCollectionLock(IEnumerable collection, Lock @lock)
        {
    #pragma warning disable CS9216
            ArgumentNullException.ThrowIfNull(collection);
            ArgumentNullException.ThrowIfNull(@lock);
            // We know the lock will only be used by our static callback, so suppress warning CS9216:
            // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/lock-semantics#lock-warning
            BindingOperations.EnableCollectionSynchronization(collection, @lock, callback);
    #pragma warning restore CS9216
        }
    }
    

    Then, in your view model on the UI thread, following Using BindingOperations.EnableCollectionSynchronization you would have something like:

    class ViewModel
    {
        public ObservableCollection<string> Items { get; } = new();
     
        private readonly System.Threading.Lock _itemsLock = new ();
    
        public ViewModel()
        {
            BindingOperationsExtensions.EnableCollectionLock(Items, _itemsLock);    
        }
    

    In other threads, use the _itemsLock via any of the methods shown in the Lock docs:

    Note that, according to the reference source, the callback currently seems to be called via an untyped Invoke(), so if you are concerned with performance you might want to profile.