C# WPF Desktop App - List - Threading and locking problem - Concept

Markus Freitag 2,961 Reputation points
2023-01-17T15:55:47.13+00:00

Hello,

I have it.

_CONCEPT_1

UI and WorkerThread access the same list. This is important. How can I prevent an exception from occurring. Insertion and extraction at the same time. Or modify.
It is an application one exe.

I fill the list via the user interface, the working thread processes the list. I don't know how to lock this.

Same Exe, is a lock enough?

Batch Quantity is for example 100

I am processing the hundredth product, after that I have to delete it from the list.

Meanwhile the operator adds new batches.

public void ListProcess(int mode)
{
	lock( ListProcess.SyncRoot )
	{
		  switch (mode)
		  {
		  default:
			 Exception
			  break;
		  case 1: // fill
			 break;
		  case 2: // modify
			 break;
		  case 3: // delete
			 break;
		  }
	}
}

Are both processes allowed to call only one function? How do I achieve the locking correctly. Thanks for examples in advance.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,184 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
6,992 questions
No comments
{count} votes

Accepted answer
  1. Michael Taylor 38,026 Reputation points
    2023-01-17T16:58:29.4833333+00:00

    If this is the same process (note same EXE doesn't matter) then a lock would be fine since it is all in the same memory space. You also tagged this as ASP.NET which changes everything if this is actually a web app.

    We already have concurrent structures that may help. Also note that SyncRoot isn't really useful anymore and probably shouldn't be used. There is no benefit. It was a v1 threading solution but you gain nothing by using it.

    If you have a producer-consumer situation where one or more threads add data to be processed and a single consumer that processes the data then a ConcurrentQueue might be a good starting solution. It already handles concurrency.

    If you want to stick with a traditional List<T> then you would need to lock the list when adding or reading. The general recommendation is to wrap this list management in a helper class. Inside the helper class, lock the list when working with it.

    class WorkerQueue
    {  
       public void Add ( MyData item )
       {
          lock(_items) 
          { 
             _items.Enqueue(item);
          };
       }
    
       public MyData GetNext ()
       {
          lock(_items)
          { 
             if (_items.Count > 0)
                return _items.Dequeue();
    
             return null;
          };  
       }
    
       private readonly Queue<MyData> _items = new Queue<MyData>();
    }
    

    Note that this is just reinventing what ConcurrentQueue already does. Perhaps a better option is to have the processor manage the queue and the other process just adds work to it.

    Also note that durability may be important. For example if something fails during processing of the work then the item has already been removed from the queue. Either that work would be lost or your worker process needs to recover and try again, report an error, etc.


2 additional answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 28,196 Reputation points
    2023-01-17T16:54:20.7233333+00:00

    each thread that accesses the list must use the same lock object. you should keep the lock as localized to list access as possible. all access to list properties and methods must be inside the lock.

    you could also just use thread safe collections:

    [https://learn.microsoft.com/en-us/dotnet/standard/collections/thread-safe/


  2. Hui Liu-MSFT 14,721 Reputation points Microsoft Employee
    2023-01-19T08:15:59.0266667+00:00

    You could try to use bindingOperations.enableCollectionSyNChronization Method .

    The documentation is pretty clear on this:

    To use a collection on multiple threads, one of which is the UI thread that owns the ItemsControl, an application has the following responsibilities:

    • Choose a synchronization mechanism.
    • Synchronize all access from the application to the collection using that mechanism.
    • Call EnableCollectionSynchronization to inform WPF of the mechanism.
    • ...

    You could lock the collection when modifying it from another thread.

    On dispatcher (UI) thread:

    _itemsLock = new object();
    Items = new ObservableCollection<Item>();
    BindingOperations.EnableCollectionSynchronization(Items, _itemsLock);
    

    Then from another thread:

    lock (_itemsLock)
    {
    
    // Once locked, you can manipulate the collection safely from another thread
    Items.Add(new Item());
    }
    
    

    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our [documentation][5] to enable e-mail notifications if you want to receive the related email notification for this thread.