Recent

Author Topic: Threads and Lock single object from mutiple objects  (Read 1098 times)

Zaher

  • Hero Member
  • *****
  • Posts: 683
    • parmaja.org
Threads and Lock single object from mutiple objects
« on: July 18, 2024, 02:56:41 pm »
Hi

I have threads (T1, T2 ...Tn) and Objects (O1, O2, O3 ... On)

I want when if T1 want to change in O1, lock it and let other thread that want to change the same object (O1) to wait, but thread who wants to change the other objects not need to wait.

By using one CS: TCriticalSection for all, Lock here will block all threads until T1 release the CS.
Or using O1.CS , TCriticalSection for each object that mean I harm the system with CS limitation with uneded CS

I have 2 ideas

Createing O1.CS as needed and free it when none thread locking, maybe use global CS to lock before create/free local CS.

or make a one CS but wrapped with a class have a list objects that locked, here i will CS.Enter if the object exists in the list, or add it to the list then CS.Enter, same as leaving.

Is there any else idea to add?

440bx

  • Hero Member
  • *****
  • Posts: 4727
Re: Threads and Lock single object from mutiple objects
« Reply #1 on: July 18, 2024, 04:26:59 pm »
The solution to the problem you presented is to have one critical section for each object _AND_ this is very important: the sequence in which the critical sections are acquired by the threads MUST be pre-established and always be the same.

IOW, if there are n critical sections, you establish an order in which they must be acquired, for example's sake, it may be: cs1, cs2, cs3...csN.  This means if a thread needs control over object cs2 and cs7, it must acquire cs2 first then acquire cs7.  Never should any thread acquire cs7 first then attempt to acquire cs2 (the different sequence is what produces deadlocks.)

To maximize concurrency, it is usually best to have an all or nothing policy, that is, if a thread needs cs2, cs4 and cs7 and has acquired cs2 and cs4 but failed to acquire cs7, release cs2 and cs4 and try again, holding cs2 and cs4 while waiting for cs7 causes other threads interested in cs7 to also wait.  releasing cs2 and cs4 gives other threads the opportunity to obtain them.

The "downside", if it can be called that, of that method is that it is not a first come first serve, it is basically random, a "lucky" thread will "win" all the locks because it tried at the "right time".    As long as the threads hold the locks for roughly equal times, this will work fine.  It could be a problem if some thread(s) hold the locks for much longer than other threads.

HTH.
« Last Edit: July 18, 2024, 04:28:48 pm by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 16144
  • Censorship about opinions does not belong here.
Re: Threads and Lock single object from mutiple objects
« Reply #2 on: July 18, 2024, 05:03:17 pm »
Yes, great answer and with good explanation.
If I smell bad code it usually is bad code and that includes my own code.

440bx

  • Hero Member
  • *****
  • Posts: 4727
Re: Threads and Lock single object from mutiple objects
« Reply #3 on: July 18, 2024, 05:11:34 pm »
Thank you for the kind words Thaddy.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Khrys

  • Full Member
  • ***
  • Posts: 104
Re: Threads and Lock single object from mutiple objects
« Reply #4 on: July 19, 2024, 09:35:35 am »
What about futexes (fast userspace mutexes)?

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. {$modeswitch advancedrecords}
  3.  
  4. interface
  5.  
  6. type
  7.   generic TFutex<T> = record
  8.   type
  9.     TSelf = specialize TFutex<T>;
  10.   private
  11.     _Data: T;
  12.     LockHolder: Integer;
  13.     function Data_Get(): T;
  14.     procedure Data_Set(const Value: T);
  15.   public
  16.     constructor Create(const Underlying: T);
  17.     class operator :=(const Underlying: T): TSelf;
  18.     class operator :=(const Self: TSelf): T;
  19.     procedure Lock();
  20.     procedure Unlock();
  21.     property Data: T read Data_Get write Data_Set;
  22.   end;
  23.  
  24. implementation
  25.  
  26. function TFutex.Data_Get(): T;
  27. begin
  28.   if InterlockedCompareExchange(LockHolder, 0, 0) <> ThreadID then begin
  29.     raise Exception.Create('Read from externally locked object');
  30.   end;
  31.   Result := _Data;
  32. end;
  33.  
  34. procedure TFutex.Data_Set(const Value: T);
  35. begin
  36.   if InterlockedCompareExchange(LockHolder, 0, 0) <> ThreadID then begin
  37.     raise Exception.Create('Write to externally locked object');
  38.   end;
  39.   _Data := Value;
  40. end;
  41.  
  42. constructor TFutex.Create(const Underlying: T);
  43. begin
  44.   LockHolder := 0;
  45.   _Data := Underlying;
  46. end;
  47.  
  48. class operator TFutex.:=(const Underlying: T): TSelf;
  49. begin
  50.   Result := TSelf.Create(Underlying);
  51. end;
  52.  
  53. class operator TFutex.:=(const Self: TSelf): T;
  54. begin
  55.   Result := Self._Data;
  56. end;
  57.  
  58. procedure TFutex.Lock();
  59. var
  60.   TID: Integer;
  61. begin
  62.   TID := ThreadID;
  63.   while InterlockedCompareExchange(LockHolder, TID, 0) <> TID do begin
  64.     ThreadSwitch();
  65.   end;
  66. end;
  67.  
  68. procedure TFutex.Unlock();
  69. var
  70.   PastLockHolder: Integer;
  71.   TID: Integer;
  72. begin
  73.   TID := ThreadID;
  74.   PastLockHolder := InterlockedCompareExchange(LockHolder, 0, TID);
  75.   if (PastLockHolder <> 0) and (PastLockHolder <> TID) then begin
  76.     raise Exception.Create('Unlock of externally locked object');
  77.   end;
  78. end;

Here's a quick example:

Code: Pascal  [Select][+][-]
  1. var
  2.   O1: specialize TFutex<TStringList>;
  3.  
  4. procedure Main();
  5. begin
  6.   O1 := TStringList.Create();
  7.   // (in another thread)
  8.   O1.Lock();
  9.   O1.Data.Add('Used O1 from ' + ThreadID.ToString());    // Use the "Data" member to checks for lock presence
  10.   O1.Unlock();
  11.   // (in main thread)
  12.   TStringList(O1).Free();                                // Use a direct cast to bypass lock checks
  13. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 16144
  • Censorship about opinions does not belong here.
Re: Threads and Lock single object from mutiple objects
« Reply #5 on: July 19, 2024, 09:47:04 am »
That is a good answer too, but is not cross-platform, alas.
If I smell bad code it usually is bad code and that includes my own code.

Zaher

  • Hero Member
  • *****
  • Posts: 683
    • parmaja.org
Re: Threads and Lock single object from mutiple objects
« Reply #6 on: July 20, 2024, 02:42:31 pm »
but is not cross-platform, alas.

Why not cross-platform? I am thinking to use this, it also work in Delphi by using TInterlocked.CompareExchange

 

TinyPortal © 2005-2018