ThreadStaticAttribute Класс

Определение

Указывает, что значение статического поля уникально для каждого потока.

public ref class ThreadStaticAttribute : Attribute
[System.AttributeUsage(System.AttributeTargets.Field, Inherited=false)]
public class ThreadStaticAttribute : Attribute
[System.AttributeUsage(System.AttributeTargets.Field, Inherited=false)]
[System.Serializable]
public class ThreadStaticAttribute : Attribute
[System.AttributeUsage(System.AttributeTargets.Field, Inherited=false)]
[System.Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public class ThreadStaticAttribute : Attribute
[<System.AttributeUsage(System.AttributeTargets.Field, Inherited=false)>]
type ThreadStaticAttribute = class
    inherit Attribute
[<System.AttributeUsage(System.AttributeTargets.Field, Inherited=false)>]
[<System.Serializable>]
type ThreadStaticAttribute = class
    inherit Attribute
[<System.AttributeUsage(System.AttributeTargets.Field, Inherited=false)>]
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type ThreadStaticAttribute = class
    inherit Attribute
Public Class ThreadStaticAttribute
Inherits Attribute
Наследование
ThreadStaticAttribute
Атрибуты

Примеры

В следующем примере создается генератор случайных чисел, создается десять потоков в дополнение к основному потоку, а затем создается два миллиона случайных чисел в каждом потоке. Он использует ThreadStaticAttribute атрибут для вычисления суммы и количества случайных чисел на поток. Он также определяет два дополнительных поля для каждого потока и previous abnormalпозволяет обнаруживать повреждение генератора случайных чисел.

using System;
using System.Threading;

public class Example
{
   [ThreadStatic] static double previous = 0.0;
   [ThreadStatic] static double sum = 0.0;
   [ThreadStatic] static int calls = 0;
   [ThreadStatic] static bool abnormal;
   static int totalNumbers = 0;
   static CountdownEvent countdown;
   private static Object lockObj;
   Random rand;
   
   public Example()
   { 
      rand = new Random();
      lockObj = new Object();
      countdown = new CountdownEvent(1);
   } 

   public static void Main()
   {
      Example ex = new Example();
      Thread.CurrentThread.Name = "Main";
      ex.Execute();
      countdown.Wait();
      Console.WriteLine("{0:N0} random numbers were generated.", totalNumbers);
   }

   private void Execute()
   {   
      for (int threads = 1; threads <= 10; threads++)
      {
         Thread newThread = new Thread(new ThreadStart(this.GetRandomNumbers));
         countdown.AddCount();
         newThread.Name = threads.ToString();
         newThread.Start();
      }
      this.GetRandomNumbers();
   }

   private void GetRandomNumbers()
   {
      double result = 0.0;

      for (int ctr = 0; ctr < 2000000; ctr++)
      {
         lock (lockObj) {
            result = rand.NextDouble();
            calls++;
            Interlocked.Increment(ref totalNumbers);
            // We should never get the same random number twice.
            if (result == previous) {
               abnormal = true;
               break;
            }
            else {
               previous = result;
               sum += result;
            }   
         }
      }
      // get last result
      if (abnormal)
         Console.WriteLine("Result is {0} in {1}", previous, Thread.CurrentThread.Name);
       
      Console.WriteLine("Thread {0} finished random number generation.", Thread.CurrentThread.Name);
      Console.WriteLine("Sum = {0:N4}, Mean = {1:N4}, n = {2:N0}\n", sum, sum/calls, calls);        
      countdown.Signal();
   }
}
// The example displays output similar to the following:
//    Thread 1 finished random number generation.
//    Sum = 1,000,556.7483, Mean = 0.5003, n = 2,000,000
//    
//    Thread 6 finished random number generation.
//    Sum = 999,704.3865, Mean = 0.4999, n = 2,000,000
//    
//    Thread 2 finished random number generation.
//    Sum = 999,680.8904, Mean = 0.4998, n = 2,000,000
//    
//    Thread 10 finished random number generation.
//    Sum = 999,437.5132, Mean = 0.4997, n = 2,000,000
//    
//    Thread 8 finished random number generation.
//    Sum = 1,000,663.7789, Mean = 0.5003, n = 2,000,000
//    
//    Thread 4 finished random number generation.
//    Sum = 999,379.5978, Mean = 0.4997, n = 2,000,000
//    
//    Thread 5 finished random number generation.
//    Sum = 1,000,011.0605, Mean = 0.5000, n = 2,000,000
//    
//    Thread 9 finished random number generation.
//    Sum = 1,000,637.4556, Mean = 0.5003, n = 2,000,000
//    
//    Thread Main finished random number generation.
//    Sum = 1,000,676.2381, Mean = 0.5003, n = 2,000,000
//    
//    Thread 3 finished random number generation.
//    Sum = 999,951.1025, Mean = 0.5000, n = 2,000,000
//    
//    Thread 7 finished random number generation.
//    Sum = 1,000,844.5217, Mean = 0.5004, n = 2,000,000
//    
//    22,000,000 random numbers were generated.
open System
open System.Threading

type Example() =
    [<ThreadStatic; DefaultValue>] 
    static val mutable private previous : double

    [<ThreadStatic; DefaultValue>] 
    static val mutable private sum : double
    
    [<ThreadStatic; DefaultValue>] 
    static val mutable private calls : int

    [<ThreadStatic; DefaultValue>] 
    static val mutable private abnormal : bool
   
    static let mutable totalNumbers = 0
    static let countdown = new CountdownEvent(1)
    static let lockObj = obj ()
    let rand = Random()


    member this.Execute() =
        for threads = 1 to 10 do
            let newThread = new Thread(ThreadStart this.GetRandomNumbers)
            countdown.AddCount()
            newThread.Name <- threads.ToString()
            newThread.Start()
        this.GetRandomNumbers()
        countdown.Wait()
        printfn $"{totalNumbers:N0} random numbers were generated."

    member _.GetRandomNumbers() =
        let mutable i = 0
        while i < 2000000 do
            lock lockObj (fun () ->
                let result = rand.NextDouble()
                Example.calls <- Example.calls + 1
                Interlocked.Increment &totalNumbers |> ignore
                // We should never get the same random number twice.
                if result = Example.previous then
                    Example.abnormal <- true
                    i <- 2000001 // break
                else
                    Example.previous <- result
                    Example.sum <- Example.sum + result )
            i <- i + 1
        // get last result
        if Example.abnormal then
            printfn $"Result is {Example.previous} in {Thread.CurrentThread.Name}"
        
        printfn $"Thread {Thread.CurrentThread.Name} finished random number generation."
        printfn $"Sum = {Example.sum:N4}, Mean = {Example.sum / float Example.calls:N4}, n = {Example.calls:N0}\n"
        countdown.Signal() |> ignore

let ex = Example()
Thread.CurrentThread.Name <- "Main"
ex.Execute()

// The example displays output similar to the following:
//    Thread 1 finished random number generation.
//    Sum = 1,000,556.7483, Mean = 0.5003, n = 2,000,000
//    
//    Thread 6 finished random number generation.
//    Sum = 999,704.3865, Mean = 0.4999, n = 2,000,000
//    
//    Thread 2 finished random number generation.
//    Sum = 999,680.8904, Mean = 0.4998, n = 2,000,000
//    
//    Thread 10 finished random number generation.
//    Sum = 999,437.5132, Mean = 0.4997, n = 2,000,000
//    
//    Thread 8 finished random number generation.
//    Sum = 1,000,663.7789, Mean = 0.5003, n = 2,000,000
//    
//    Thread 4 finished random number generation.
//    Sum = 999,379.5978, Mean = 0.4997, n = 2,000,000
//    
//    Thread 5 finished random number generation.
//    Sum = 1,000,011.0605, Mean = 0.5000, n = 2,000,000
//    
//    Thread 9 finished random number generation.
//    Sum = 1,000,637.4556, Mean = 0.5003, n = 2,000,000
//    
//    Thread Main finished random number generation.
//    Sum = 1,000,676.2381, Mean = 0.5003, n = 2,000,000
//    
//    Thread 3 finished random number generation.
//    Sum = 999,951.1025, Mean = 0.5000, n = 2,000,000
//    
//    Thread 7 finished random number generation.
//    Sum = 1,000,844.5217, Mean = 0.5004, n = 2,000,000
//    
//    22,000,000 random numbers were generated.
Imports System.Threading

Public Class Example
   <ThreadStatic> Shared previous As Double = 0.0
   <ThreadStatic> Shared sum As Double = 0.0
   <ThreadStatic> Shared calls As Integer = 0
   <ThreadStatic> Shared abnormal As Boolean
   Shared totalNumbers As Integer = 0
   Shared countdown As CountdownEvent
   Private Shared lockObj As Object
   Dim rand As Random

   Public Sub New()
      rand = New Random()
      lockObj = New Object()
      countdown = New CountdownEvent(1)
   End Sub

   Public Shared Sub Main()
      Dim ex As New Example()
      Thread.CurrentThread.Name = "Main"
      ex.Execute()
      countdown.Wait()
      Console.WriteLine("{0:N0} random numbers were generated.", totalNumbers)
   End Sub

   Private Sub Execute()
      For threads As Integer = 1 To 10
         Dim newThread As New Thread(New ThreadStart(AddressOf GetRandomNumbers))
         countdown.AddCount()
         newThread.Name = threads.ToString()
         newThread.Start()
      Next
      Me.GetRandomNumbers()
   End Sub

   Private Sub GetRandomNumbers()
      Dim result As Double = 0.0
      
       
      For ctr As Integer = 1 To 2000000
         SyncLock lockObj
            result = rand.NextDouble()
            calls += 1
            Interlocked.Increment(totalNumbers)
            ' We should never get the same random number twice.
            If result = previous Then
               abnormal = True
               Exit For
            Else
               previous = result
               sum += result
            End If   
         End SyncLock
      Next
      ' Get last result.
      If abnormal Then
         Console.WriteLine("Result is {0} in {1}", previous, Thread.CurrentThread.Name)
      End If       
      
      Console.WriteLine("Thread {0} finished random number generation.", Thread.CurrentThread.Name)
      Console.WriteLine("Sum = {0:N4}, Mean = {1:N4}, n = {2:N0}", sum, sum/calls, calls)
      Console.WriteLine()        
      countdown.Signal()
   End Sub
End Class
' The example displays output similar to the following:
'    Thread 1 finished random number generation.
'    Sum = 1,000,556.7483, Mean = 0.5003, n = 2,000,000
'    
'    Thread 6 finished random number generation.
'    Sum = 999,704.3865, Mean = 0.4999, n = 2,000,000
'    
'    Thread 2 finished random number generation.
'    Sum = 999,680.8904, Mean = 0.4998, n = 2,000,000
'    
'    Thread 10 finished random number generation.
'    Sum = 999,437.5132, Mean = 0.4997, n = 2,000,000
'    
'    Thread 8 finished random number generation.
'    Sum = 1,000,663.7789, Mean = 0.5003, n = 2,000,000
'    
'    Thread 4 finished random number generation.
'    Sum = 999,379.5978, Mean = 0.4997, n = 2,000,000
'    
'    Thread 5 finished random number generation.
'    Sum = 1,000,011.0605, Mean = 0.5000, n = 2,000,000
'    
'    Thread 9 finished random number generation.
'    Sum = 1,000,637.4556, Mean = 0.5003, n = 2,000,000
'    
'    Thread Main finished random number generation.
'    Sum = 1,000,676.2381, Mean = 0.5003, n = 2,000,000
'    
'    Thread 3 finished random number generation.
'    Sum = 999,951.1025, Mean = 0.5000, n = 2,000,000
'    
'    Thread 7 finished random number generation.
'    Sum = 1,000,844.5217, Mean = 0.5004, n = 2,000,000
'    
'    22,000,000 random numbers were generated.

В примере используется lock инструкция в C#, lock функция в F#, а SyncLock также конструкция в Visual Basic для синхронизации доступа к генератору случайных чисел. Это предотвращает повреждение генератора случайных чисел, что обычно приводит к возвращению нулевых значений для всех последующих вызовов.

В примере также используется CountdownEvent класс, чтобы убедиться, что каждый поток завершил создание случайных чисел перед отображением общего числа вызовов. В противном случае, если основной поток завершает выполнение до дополнительных потоков, которые он создает, отображается неточное значение для общего числа вызовов методов.

Комментарии

Поле, помеченное static как ThreadStaticAttribute не совместное использование между потоками. Каждый выполняемый поток имеет отдельный экземпляр поля, а также независимо задает и получает значения для этого поля. Если доступ к полю осуществляется в другом потоке, он будет содержать другое значение.

Обратите внимание, что помимо применения атрибута ThreadStaticAttribute к полю необходимо также определить его как static поле (в C# или F#) или Shared поле (в Visual Basic).

Примечание

Не указывайте начальные значения для полей, помеченных как ThreadStaticAttributeодин раз, так как такая инициализация происходит только один раз, когда конструктор класса выполняется и, следовательно, влияет только на один поток. Если не указать начальное значение, можно использовать поле, инициализируемого в его значение по умолчанию, если это тип значения, или ссылочного null типа.

Используйте этот атрибут как есть и не наследуйте его.

Дополнительные сведения об использовании атрибутов см. в разделе "Атрибуты".

Конструкторы

ThreadStaticAttribute()

Инициализирует новый экземпляр класса ThreadStaticAttribute.

Свойства

TypeId

В случае реализации в производном классе возвращает уникальный идентификатор для этого атрибута Attribute.

(Унаследовано от Attribute)

Методы

Equals(Object)

Возвращает значение, показывающее, равен ли экземпляр указанному объекту.

(Унаследовано от Attribute)
GetHashCode()

Возвращает хэш-код данного экземпляра.

(Унаследовано от Attribute)
GetType()

Возвращает объект Type для текущего экземпляра.

(Унаследовано от Object)
IsDefaultAttribute()

При переопределении в производном классе указывает, является ли значение этого экземпляра значением по умолчанию для производного класса.

(Унаследовано от Attribute)
Match(Object)

При переопределении в производном классе возвращает значение, указывающее, является ли этот экземпляр равным заданному объекту.

(Унаследовано от Attribute)
MemberwiseClone()

Создает неполную копию текущего объекта Object.

(Унаследовано от Object)
ToString()

Возвращает строку, представляющую текущий объект.

(Унаследовано от Object)

Явные реализации интерфейса

_Attribute.GetIDsOfNames(Guid, IntPtr, UInt32, UInt32, IntPtr)

Сопоставляет набор имен соответствующему набору идентификаторов диспетчеризации.

(Унаследовано от Attribute)
_Attribute.GetTypeInfo(UInt32, UInt32, IntPtr)

Возвращает сведения о типе объекта, которые можно использовать для получения сведений о типе интерфейса.

(Унаследовано от Attribute)
_Attribute.GetTypeInfoCount(UInt32)

Возвращает количество предоставляемых объектом интерфейсов для доступа к сведениям о типе (0 или 1).

(Унаследовано от Attribute)
_Attribute.Invoke(UInt32, Guid, UInt32, Int16, IntPtr, IntPtr, IntPtr, IntPtr)

Предоставляет доступ к открытым свойствам и методам объекта.

(Унаследовано от Attribute)

Применяется к

См. также раздел