Open sandboxFocusImprove this doc

THR027: Static Field Is Not of a Thread-Safe Type

Severity Warning
Reported by PostSharp.Patterns.Threading
Message Thread-safety policy: static field <Field> must be of a thread-safe type, or be marked as [ExplicitlySynchronized] or [ThreadStatic].

Cause

A static field is shared by all threads. A readonly field always refers to the same object, but threads can still modify this object concurrently. When you add the ThreadSafetyPolicy attribute to an assembly, PostSharp therefore checks the type of the static fields of every class and reports THR027 when it is not thread-safe. A type is thread-safe for the policy when it has no instance fields, when it has a threading model, when it is marked as ExplicitlySynchronizedAttribute, or when the type analyzer of the threading library recognizes it as immutable, as string and the primitive types.

The collections of .NET, such as Dictionary<TKey,TValue>, have mutable fields and no threading model, so the policy reports static fields of these types, even when your code synchronizes the access to them.

The policy does not check constants, compiler-generated fields, fields marked as ExplicitlySynchronizedAttribute and fields marked as ThreadStaticAttribute. A field of a type that is declared in a referenced assembly has a threading model only when PostSharp processed that assembly, and the policy does not see ExplicitlySynchronizedAttribute on a type of a referenced assembly.

In the following example, the rates field is readonly, but its type is Dictionary<string, decimal>.

using System;
using System.Collections.Generic;
using PostSharp.Patterns.Threading;

[assembly: ThreadSafetyPolicy]

namespace Errors.THR027_Error;

public static class ExchangeRates
{
    private static readonly object sync = new();

    // The field is read-only, but a Dictionary is not thread-safe, so the policy reports the field.
    private static readonly Dictionary<string, decimal> rates = new();

    public static void SetRate( string currency, decimal rate )
    {
        lock ( sync )
        {
            rates[currency] = rate;
        }
    }

    public static decimal GetRate( string currency )
    {
        lock ( sync )
        {
            return rates[currency];
        }
    }
}

public static class Program
{
    public static void Main()
    {
        ExchangeRates.SetRate( "USD", 1.08m );
        Console.WriteLine( $"USD rate: {ExchangeRates.GetRate( "USD" )}" );
    }
}
warning THR027: THR027_Error.cs(14,57): Thread-safety policy: static field Errors.THR027_Error.ExchangeRates.rates must be of a thread-safe type, or be marked as [ExplicitlySynchronized] or [ThreadStatic].

Resolution

  • When every access to the field is synchronized in your code, for example with a lock statement, mark the field as ExplicitlySynchronizedAttribute.
  • When the type of the field is declared in your project, apply a threading model to it.
  • When each thread needs its own object, mark the field as ThreadStaticAttribute.

In the following example, the ExchangeRates class accesses the dictionary under a lock, and the field is marked as ExplicitlySynchronizedAttribute.

using System;
using System.Collections.Generic;
using PostSharp.Patterns.Threading;

[assembly: ThreadSafetyPolicy]

namespace Errors.THR027_Fixed;

public static class ExchangeRates
{
    private static readonly object sync = new();

    // Every access to the Dictionary holds the lock, and [ExplicitlySynchronized] tells the policy so.
    [ExplicitlySynchronized]
    private static readonly Dictionary<string, decimal> rates = new();

    public static void SetRate( string currency, decimal rate )
    {
        lock ( sync )
        {
            rates[currency] = rate;
        }
    }

    public static decimal GetRate( string currency )
    {
        lock ( sync )
        {
            return rates[currency];
        }
    }
}

public static class Program
{
    public static void Main()
    {
        ExchangeRates.SetRate( "USD", 1.08m );
        Console.WriteLine( $"USD rate: {ExchangeRates.GetRate( "USD" )}" );
    }
}
USD rate: 1.08

See Also

Reference

ThreadSafetyPolicy
ExplicitlySynchronizedAttribute

Other Resources

Making a Whole Project or Solution Thread Safe
Writing Thread-Safe Code with Threading Models