Collapse the table of content
Expand the table of content
Expand Minimize

ConcurrencyMode Enumeration

Specifies whether a service class supports single-threaded or multi-threaded modes of operation.

Namespace:  System.ServiceModel
Assembly:  System.ServiceModel (in System.ServiceModel.dll)

public enum ConcurrencyMode

Member nameDescription
MultipleThe service instance is multi-threaded. No synchronization guarantees are made. Because other threads can change your service object at any time, you must handle synchronization and state consistency at all times.
ReentrantThe service instance is single-threaded and accepts reentrant calls. The reentrant service accepts calls when you call another service; it is therefore your responsibility to leave your object state consistent before callouts and you must confirm that operation-local data is valid after callouts. Note that the service instance is unlocked only by calling another service over a WCF channel. In this case, the called service can reenter the first service via a callback. If the first service is not reentrant, the sequence of calls results in a deadlock. For details, see ConcurrencyMode.
SingleThe service instance is single-threaded and does not accept reentrant calls. If the InstanceContextMode property is Single, and additional messages arrive while the instance services a call, these messages must wait until the service is available or until the messages time out.

ConcurrencyMode is used in conjunction with the ConcurrencyMode property to specify whether a service class supports single-threaded or multi-threaded modes of operation. A single-threaded operation can be either reentrant or non-reentrant.

The following table shows when Windows Communication Foundation (WCF) permits an operation to be invoked while another one is in progress, depending upon the ConcurrencyMode.

ConcurrencyMode Value

Can a new operation be invoked?

Single

Never.

Reentrant

Only while invoking another service or a callback.

Multiple

Always.

The following code example demonstrates the different between using Single, Reentrant, and Multiple. This sample does not compile without a real implementation behind it, but does demonstrate the kind of threading guarantees that WCF makes and what that means for your operation code.

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;
  }
}

.NET Framework

Supported in: 4.6, 4.5, 4, 3.5, 3.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1
Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2015 Microsoft