Login Login
MORE

WIDGETS

Widgets

Wanted articles
Who is online?
Article tools

CSharp:Multithreading gestire la concorrenza

From Aino Wiki

Jump to: navigation, search

Per fare debug vedere suggerimenti: Tips dall'IDE di Visual Studio

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)
        {
            ...
        }
    }
}
e
internal class MyClass

{

   [MethodImpl(MethodImplOptions.Synchronized)]
   public void DoSomething() 
   {
       ...
   }
   [MethodImpl(MethodImplOptions.Synchronized)]
   public void DoSomethingElse() 
   {
       ...
   }
}
Quest'ultima tecnica garantisce una esecuzione per volta ma qualora non sia stato possibile effettuare una esecuzione questa viene messa in coda ed eseguita comunque appena possibile! Quindi può provocare dei DeadLock!

Mappa e Links


C# | Multithreading


Visual Studio

Author Giuseppe AINO