Difference between revisions of "CSharp:Multithreading gestire la concorrenza"
From Aino Wiki
(No difference)
|
Latest revision as of 21:40, 8 June 2022
Per fare debug vedere suggerimenti: Tips dall'IDE di Visual Studio
Contents
Bloccare una porzione di codice
Teoria sulle best practices da: Microsoft
Doc: Microsoft
Segue esempio su come bloccare il codice affinchè un solo thread alla volta possa eseguire quella porzione, è un semaforo affinchè la risorsa condivisa sia assegnata ad un solo thead tra i concorrenti.
L'oggetto Monitor mediante in metodo TryEnter usa un intervallo di tempo di 300 millisecondi entro il quale ottenere il semaforo verde altrimenti restituendo "false" ed il blocco non sarà eseguito.Questo è un accorgimento per evitare i DeadLocks.
Uso di Monitor
if (Monitor.TryEnter(lockObject, 300)) { try { // Place code protected by the Monitor here. } finally { Monitor.Exit(this); } } else { // Code to execute if the attempt times out. }
Si può usare una variabile booleana per far compiere azioni in funzione del fatto se si è trovato libero o occupato il blocco del Monitor.
var lockObj = new Object(); int timeout = 500; bool lockTaken = false; try { Monitor.TryEnter(lockObj, timeout, ref lockTaken); if (lockTaken) { // The critical section. } else { // The lock was not acquired. } } finally { // Ensure that the lock is released. if (lockTaken) { Monitor.Exit(lockObj); } }
ATTENZIONE che quel che si potrà modificare deve essere una Property contenuta nell'oggetto "lockObject" e NON "lockObject" altrimenti si ottiene una eccezione!
L'errore a cui mi riferisco è: "Object synchronization method was called from an unsynchronized block of code".Al posto dei 300 millisecondi si può inserire new TimeSpan(0, 0, 2)
ciò consentirà di attendere esattamente per 2 secondi. "lockObject" è l'oggetto sul quale limitare l'accesso.
Es concreto
StartJob()
è il metodo chiamato da un WatchDog che verifica se la sua esecuzione è andata ok o meno.
public class MyClass { private static object m_lockObjStartJob = new object(); // <---- private static Logger m_logger = LogManager.GetCurrentClassLogger(); public bool IsStarted { [MethodImpl(MethodImplOptions.Synchronized)] get; [MethodImpl(MethodImplOptions.Synchronized)] set; } //volatile /// <summary> /// Indica se il Thread è abilitato. Posto a false dalle richieste di Stop del processo /// </summary> public bool Enabled { //[MethodImpl(MethodImplOptions.Synchronized)] get; //[MethodImpl(MethodImplOptions.Synchronized)] set; } //volatile public void StartJob() { string strMsg4Log = string.Empty; try// <---- { if (Monitor.TryEnter(m_lockObjStartJob, 500))// <---- { try { #region Core // ... #endregion } catch (Exception ex) { // ... IsStarted = Enabled = false; } finally { Monitor.Exit(m_lockObjStartJob);// <---- } } else { strMsg4Log = string.Format("StartJob() is still in use, thread killed!");// <---- m_logger.Warn(strMsg4Log); } } catch (Exception ex) { // ... IsStarted = Enabled = false; } } }
Aggirare l'errore Object synchronization method was called from an unsynchronized block of code
Ci sono due modi, o metter il monitor su un livello superiore della gerarchia dell'oggetto da bloccare oppure come segue:#region Aggiornamento MODELLO if (lstAIVDM4Model.Count > 0) { if (Monitor.TryEnter(ShipData.Instance.lstAIVDM, new TimeSpan(0, 0, 2))) { try { ShipData.Instance.lstAIVDM.Clear(); ShipData.Instance.lstAIVDM.AddRange(lstAIVDM4Model); //ShipData.Instance.lstAIVDM = lstAIVDM4Model; <--- ERRORE: Object synchronization method was called from an unsynchronized block of code. m_loggerMain.Debug("OK, {0} AIS", lstAIVDM4Model.Count); } catch (Exception ex) { m_loggerMain.Error("KO {0}", ex.Message); } finally { Monitor.Exit(ShipData.Instance.lstAIVDM); } } } #endregion
Uso di Lock
try { lock (this.CacheLock[cacheKey]) { string val; this.CacheValue.TryGetValue(cacheKey, out val); if (string.IsNullOrEmpty(val)) { if (_wfHelper != null) { this.CacheValue[cacheKey] = this._wfHelper.Get(cacheKey); } else this.CacheValue[cacheKey] = string.Empty; } shipValuesManager = JSONHelper.Deserialize<ValuesManager>(this.CacheValue[cacheKey]); } } catch (Exception ex) { Logger.ErrorException(string.Format("Exception retrieve cache information from key {0}. Create new cache", cacheKey), ex); shipValuesManager = new ValuesManager(); resultCode += "ERR_CACHE,"; }
Bloccare un metodo
Come da stackoverflow ci sono diverse vie, riporto qui le due della discussione:internal class MyClass { private readonly object _syncRoot = new Object(); public void DoSomething() { lock(_syncRoot) { ... } } public void DoSomethingElse() { lock(_syncRoot) { ... } } }
internal class MyClass{
[MethodImpl(MethodImplOptions.Synchronized)] public void DoSomething() { ... } [MethodImpl(MethodImplOptions.Synchronized)] public void DoSomethingElse() { ... }}