* * *

Author Topic: Multithreading with Synaser/Synapse  (Read 395 times)

kupferstecher

  • Jr. Member
  • **
  • Posts: 68
Multithreading with Synaser/Synapse
« on: April 21, 2017, 01:06:19 pm »
Hello,

in my programm I use Synaser for serial communication and Synapse for UDP. For receiving, each port has its own thread polling BlockSerial.RecvByte (UDPBlockSocket.RecvPacket respectively). Sending is always done via main thread. In Synaser I experienced crashes, probably when the polling thread and the main thread access the BlockSerial simultaniously (but not to sure about that). Thus I added some blocking mechanism with boolean variables. I thought boolean variables are thread safe, but it seems that they are not suitable for interlocks. Using critical sections I'd like to avoid, as for each single poll the critical section has to be entered end left again (performance). An other possibility would be doing the sending from within the polling thread. But this would need an additional buffer for inter-thread communication.

So how is the correct way to use the Synaser and Synapse in Multithreading applications?

Or could there be an other issue, e.g. that I have to check if transmitting is already finished, before I poll the serial port?

Thanks!
« Last Edit: April 21, 2017, 01:07:54 pm by kupferstecher »

Blestan

  • Sr. Member
  • ****
  • Posts: 262
Re: Multithreading with Synaser/Synapse
« Reply #1 on: April 21, 2017, 02:32:59 pm »
you must use interlocked compare exchange and a spinwait in the threads to avoid using critical sections... and you must use a queue to post packets to the serial post no trying to write to the COM when the thread want but to push data to the queue and the main thread just to poll if data ca be writen to com and pull from the queue ...

kupferstecher

  • Jr. Member
  • **
  • Posts: 68
Re: Multithreading with Synaser/Synapse
« Reply #2 on: April 21, 2017, 08:59:46 pm »
Thanks for the reply! InterlockedCompareExchange I've never used before, so I first had to do some reading to understand your comment.

Would the below structure work? This would even avoid a queue.

Another question, if I have two COM ports to listen to, then I use two BlockSerial and two polling threads. can the two threads independetly poll their own BlockSerial or is there also synchronisation necessary? (As I encounter sporadic crashes I couldn't just test it).


Code: Pascal  [Select]
  1. const
  2.   ComAccessIdle = 0;
  3.   ComAccessRead = 1;
  4.   ComAccessWrite = 2;
  5.  
  6.   Type TReceiveThread = class(TThread)
  7.     //...
  8.     ComAccess: Integer;  
  9.   end;
  10.  
  11.  
  12. //---------------------------------------------------------  
  13. // Attention: Called by main thread
  14. Procedure TReceiveThread.Send(OutValue:Byte);
  15. var CurrentComAccess: Integer;
  16. begin
  17.   While not Terminated do begin
  18.     CurrentComAccess:= InterlockedCompareExchange(ComAccess,ComAccessWrite,ComAccessIdle);
  19.     if CurrentComAccess = ComAccessIdle then BREAK;
  20.   End;//while
  21.  
  22.   BlockSerial.SendByte(OutValue);
  23.  
  24.   InterlockedExchange(ComAccess,ComAccessIdle);
  25.  
  26. end;
  27.  
  28. //---------------------------------------------------------
  29. Procedure TReceiveThread.Execute;
  30. var CurrentComAccess, LastErr: Integer;
  31. begin
  32.  
  33.   While not Terminated do begin
  34.     CurrentComAccess:= InterlockedCompareExchange(ComAccess,ComAccessRead,ComAccessIdle);
  35.     if CurrentComAccess <> ComAccessIdle
  36.     then begin spinwait(10); CONTINUE; end;  
  37.  
  38.     InValue := BlockSerial.RecvByte(10);
  39.     LastErr := BlockSerial.LastError;
  40.    
  41.     InterlockedExchange(ComAccess,ComAccessIdle);
  42.    
  43.    
  44.     If LastErr <> 0 Then Begin
  45.       Sleep(10);
  46.       CONTINUE;
  47.     End;
  48.      
  49.     //Process read value
  50.     //...
  51.  
  52.   End;//While
  53.  
  54. end;


Blestan

  • Sr. Member
  • ****
  • Posts: 262
Re: Multithreading with Synaser/Synapse
« Reply #3 on: April 22, 2017, 08:10:00 am »
look you must understand atomic operation very well before you can use them ... i propose you to stick with critial sections and make code work and try to make it lock free... the best scenario is the "share nothing"  model - one socket and one com per thread ...
p.s  interlockcompare exchange is pointless on local (stack) variables ... each thread has its own current com access var. use global. second. :
var threadusingcom: tthreadhandle=0; // global

try // avoid deadlocks
 while not icx ( threadusingcom, myId, 0)<>,0 do spinwait;
send_very_quickly(somedata);
finaly
icx(thethreadusingcom,0,myId);
« Last Edit: April 22, 2017, 08:25:36 am by Blestan »

kupferstecher

  • Jr. Member
  • **
  • Posts: 68
Re: Multithreading with Synaser/Synapse
« Reply #4 on: April 22, 2017, 11:02:06 am »
look you must understand atomic operation very well before you can use them ...
Thats why I ask here.  :)
Quote
p.s  interlockcompare exchange is pointless on local (stack) variables ...
each thread has its own current com access var. use global.
In my code CurrentComAccess is just the (temp) result of icx, not the shared variable. The shared variable (private ComAccess: Integer;) is defined as variable of the thread class, is this 'global' enough?

Quote
try // avoid deadlocks
 while not icx ( threadusingcom, myId, 0)<>,0 do spinwait;
send_very_quickly(somedata);
finaly
icx(thethreadusingcom,0,myId);
I don't see the big difference here to the version I postet. The try-finally and using thread IDs are good hints though. Thanks for your help!

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus