ServiceBehaviorAttribute.ConcurrencyMode Proprietà

Definizione

Ottiene o imposta un valore che indica se un servizio supporta un solo thread, più thread o chiamate rientranti.

public:
 property System::ServiceModel::ConcurrencyMode ConcurrencyMode { System::ServiceModel::ConcurrencyMode get(); void set(System::ServiceModel::ConcurrencyMode value); };
public System.ServiceModel.ConcurrencyMode ConcurrencyMode { get; set; }
member this.ConcurrencyMode : System.ServiceModel.ConcurrencyMode with get, set
Public Property ConcurrencyMode As ConcurrencyMode

Valore della proprietà

Uno dei valori di ConcurrencyMode. Il valore predefinito è Single.

Eccezioni

Il valore non appartiene all'enumerazione ConcurrencyMode.

Esempio

Nell'esempio di codice seguente vengono illustrate le differenze tra l'utilizzo di Single, Reentrant e Multiple. Questo esempio non viene compilato senza un'implementazione reale dietro di esso, ma illustra il tipo di threading garantisce che Windows Communication Foundation (WCF) faccia e cosa significa per il codice dell'operazione.

using System;
using System.ServiceModel;

[ServiceContract]
public interface IHttpFetcher
{
  [OperationContract]
  string GetWebPage(string address);
}

// These classes have the invariant that:
//     this.slow.GetWebPage(this.cachedAddress) == this.cachedWebPage.
// When you read cached values you can assume they are valid. When
// you write the cached values, you must guarantee that they are valid.
// With ConcurrencyMode.Single, WCF does not call again into the object
// so long as the method is running. After the operation returns the object
// can be called again, so you must make sure state is consistent before
// returning.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)]
class SingleCachingHttpFetcher : IHttpFetcher
{
    string cachedWebPage;
    string cachedAddress;
    readonly IHttpFetcher slow;

    public string GetWebPage(string address)
    {
        // <-- Can assume cache is valid.
        if (this.cachedAddress == address)
        {
            return this.cachedWebPage;
        }

        // <-- Cache is no longer valid because we are changing
        // one of the values.
        this.cachedAddress = address;
        string webPage = slow.GetWebPage(address);
        this.cachedWebPage = webPage;
        // <-- Cache is valid again here.

        return this.cachedWebPage;
        // <-- Must guarantee that the cache is valid because we are returning.
    }
}

// With ConcurrencyMode.Reentrant, WCF makes sure that only one
// thread runs in your code at a time. However, when you call out on a
// channel, the operation can get called again on another thread. Therefore
// you must confirm that state is consistent both before channel calls and
// before you return.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
class ReentrantCachingHttpFetcher : IHttpFetcher
{
  string cachedWebPage;
  string cachedAddress;
  readonly SlowHttpFetcher slow;

  public ReentrantCachingHttpFetcher()
  {
    this.slow = new SlowHttpFetcher();
  }

  public string GetWebPage(string address)
  {
    // <-- Can assume that cache is valid.
    if (this.cachedAddress == address)
    {
        return this.cachedWebPage;
    }

    // <-- Must guarantee that the cache is valid, because
    // the operation can be called again before we return.
    string webPage = slow.GetWebPage(address);
    // <-- Can assume cache is valid.

    // <-- Cache is no longer valid because we are changing
    // one of the values.
    this.cachedAddress = address;
    this.cachedWebPage = webPage;
    // <-- Cache is valid again here.

    return this.cachedWebPage;
    // <-- Must guarantee that cache is valid because we are returning.
  }
}

// With ConcurrencyMode.Multiple, threads can call an operation at any time.
// It is your responsibility to guard your state with locks. If
// you always guarantee you leave state consistent when you leave
// the lock, you can assume it is valid when you enter the lock.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class MultipleCachingHttpFetcher : IHttpFetcher
{
  string cachedWebPage;
  string cachedAddress;
  readonly SlowHttpFetcher slow;
  readonly object ThisLock = new object();

  public MultipleCachingHttpFetcher()
  {
    this.slow = new SlowHttpFetcher();
  }

  public string GetWebPage(string address)
  {
    lock (this.ThisLock)
    {
      // <-- Can assume cache is valid.
      if (this.cachedAddress == address)
      {
          return this.cachedWebPage;
          // <-- Must guarantee that cache is valid because
          // the operation returns and releases the lock.
      }
      // <-- Must guarantee that cache is valid here because
      // the operation releases the lock.
    }

    string webPage = slow.GetWebPage(address);

    lock (this.ThisLock)
    {
      // <-- Can assume cache is valid.

      // <-- Cache is no longer valid because the operation
      // changes one of the values.
      this.cachedAddress = address;
      this.cachedWebPage = webPage;
      // <-- Cache is valid again here.

      // <-- Must guarantee that cache is valid because
      // the operation releases the lock.
    }

    return webPage;
  }
}

Commenti

Questa proprietà indica se un'istanza di un servizio può supportare un solo thread o più thread eseguiti contemporaneamente e, in caso di thread singolo, se è supportata la reentrancy.

Nota

La proprietà ConcurrencyMode interagisce con altre impostazioni. Ad esempio, se si imposta il valore InstanceContextMode su Single, il servizio può elaborare un solo messaggio alla volta, a meno che il valore di ConcurrencyMode non venga impostato su Multiple. Questa proprietà genera inoltre un comportamento in combinazione con la proprietà ServiceContractAttribute.SessionMode. Per informazioni dettagliate, vedere Sessioni, istanze e concorrenza.

L'impostazione di ConcurrencyMode su Single indica al sistema di limitare le istanze del servizio a un solo thread di esecuzione alla volta, evitando in questo modo di incorrere in problemi di threading. Un valore Multiple indica che gli oggetti servizio possono essere eseguiti da più thread contemporaneamente. In questo caso, è necessario garantire la sicurezza dei thread.

Reentrant limita inoltre l'accesso a un singolo thread alla volta; durante l'elaborazione dell'operazione, nessun altro messaggio può immettere l'operazione. Se durante l'operazione, viene avviata una chiamata a un'altro servizio, il messaggio corrente perde il blocco sull'operazione che diventa libera di elaborare altri messaggi. Quando la chiamata del servizio viene restituita, il blocco viene ristabilito e il messaggio originale può continuare l'elaborazione fino al termine o fino a quando non si verifica un'altra chiamata dell'operazione.

Importante

Anche se Single limita le istanze del servizio a un thread di esecuzione alla volta, è necessario impostare anche su MaxConcurrentCalls 1 per garantire l'assenza di messaggi non ordinati.

Inoltre, è responsabilità dell'utente lasciare coerente lo stato dell'oggetto prima dei callout ed è necessario verificare che i dati locali dell'operazione siano validi dopo i callout. Si noti che l'istanza del servizio viene sbloccata solo chiamando un altro servizio su un canale WCF. In questo caso, il servizio chiamato può rientrare nel primo servizio tramite un callback. Se il primo servizio non è rientrante, la sequenza di chiamate determina un deadlock. Per informazioni dettagliate, vedere ConcurrencyMode.

Durante qualsiasi chiamata in uscita da un'operazione di elaborazione, è possibile modificare i dati non locali rispetto all'operazione I dati sullo stato locale sono sicuramente validi quando il messaggio originale riprende l'elaborazione. Di conseguenza, prima della chiamata in uscita è necessario assicurarsi che i dati non locali siano validi per altre chiamate in ingresso e riconvalidare i dati non locali dopo la restituzione della chiamata in uscita.

Nello pseudo-codice seguente viene illustrato il modello necessario per il supporto delle chiamate rientranti.

public void MyMethod()
{
  this.SomeNonLocalDataState;
  // Here you need to clean nonlocal state for other users
  OutboundProxy proxy = new OutboundProxy();
  int returnValue = proxy.CallOutOfOperation();
  // Ensure that this.SomeNonLocalDataState is valid for continued use.
  this.ModifyNonLocalState;
  return returnValue;
}

Se si utilizza il modello di chiamata asincrona Begin/End per una chiamata in uscita quando la proprietà ConcurrencyMode è Reentrant, viene generata un'eccezione. Per le chiamate in uscita asincrone è necessaria un'operazione in cui ConcurrencyMode è Multiple, nel qual caso sarà necessario gestire problemi di sincronizzazione.

In genere, se arriva un messaggio per un'istanza che viola la modalità di concorrenza, il messaggio resta in attesa fino a quando l'istanza non diventa disponibile o fino al timeout.

Inoltre, se ConcurrencyMode è impostata su Single e una chiamata rientrante è bloccata in attesa che l'istanza sia disponibile, il sistema rileva il deadlock e genera un'eccezione.

Nota

Viene generata una InvalidOperationException in fase di esecuzione se la proprietà ReleaseServiceInstanceOnTransactionComplete è true quando la proprietà ConcurrencyMode è impostata su Single.

Si noti che è necessario impostare in modo esplicito la proprietà ReleaseServiceInstanceOnTransactionComplete su false se è presente un'operazione con la proprietà OperationBehaviorAttribute.TransactionScopeRequired impostata su true e si imposta la proprietà ConcurrencyMode su Reentrant. In caso contrario, viene generata un'eccezione di convalida perché il valore predefinito della proprietà ReleaseServiceInstanceOnTransactionComplete è true.

Esiste un'interazione fra la proprietà ConcurrencyMode e altre proprietà in grado di modificare il comportamento di runtime. Per una descrizione completa di queste interazioni, vedere Sessioni, istanze e concorrenza.

Si applica a