Skip to content

C# 多线程 10-并行编程模式 01-实现惰性求值的共享状态

🏷️ 《C# 多线程》

示例代码

csharp
/// <summary>
/// 实现惰性求值的共享状态
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    var t = ProcessAsynchronously();
    t.Wait();

    Console.WriteLine("Press Enter to exit");
    Console.ReadLine();
}

static async Task ProcessAsynchronously()
{
    // 不安全的对象,构造方法会被调用了多次,
    // 并且不同的线程中值是不同的
    var unsafeState = new UnsafeState();
    Task[] tasks = new Task[4];

    for (int i = 0; i < 4; i++)
    {
        tasks[i] = Task.Run(() => Worker(unsafeState));
    }

    await Task.WhenAll(tasks);
    Console.WriteLine("------------------------------");

    // 使用双重锁定模式
    var firstState = new DoubleCheckedLocking();
    for (int i = 0; i < 4; i++)
    {
        tasks[i] = Task.Run(() => Worker(firstState));
    }

    await Task.WhenAll(tasks);
    Console.WriteLine("------------------------------");

    // 使用 LazyInitializer.EnsureInitialized 方法
    var secondState = new BCLDoubleChecked();
    for (int i = 0; i < 4; i++)
    {
        tasks[i] = Task.Run(() => Worker(secondState));
    }

    await Task.WhenAll(tasks);
    Console.WriteLine("------------------------------");

    // 使用 Lazy<T>类型
    var lazy = new Lazy<ValueToAccess>(Compute);
    var thirdState = new LazyWrapper(lazy);
    for (int i = 0; i < 4; i++)
    {
        tasks[i] = Task.Run(() => Worker(thirdState));
    }

    await Task.WhenAll(tasks);
    Console.WriteLine("------------------------------");

    // 使用 LazyInitializer.EnsureInitialized 方法的一个不使用锁的重载
    var fourthState = new BCLThreadSafeFactory();
    for (int i = 0; i < 4; i++)
    {
        tasks[i] = Task.Run(() => Worker(fourthState));
    }

    await Task.WhenAll(tasks);
    Console.WriteLine("------------------------------");
}

private static void Worker(IHasValue state)
{
    Console.WriteLine($"Worker runs on thread id {Thread.CurrentThread.ManagedThreadId}");
    Console.WriteLine($"State value: {state.Value.Text}");
}

class UnsafeState : IHasValue
{
    private ValueToAccess _value;

    // 不安全的对象,构造方法 Compute 会被调用多次
    public ValueToAccess Value => _value ?? (_value = Compute());
}

class DoubleCheckedLocking : IHasValue
{
    private readonly object _syncRoot = new object();
    private volatile ValueToAccess _value;
    
    public ValueToAccess Value
    {
        get
        {
            // 使用锁及双重验证,确保构造方法 Compute 仅执行一次
            if (_value == null)
            {
                lock (_syncRoot)
                {
                    if (_value == null)
                    {
                        _value = Compute();
                    }
                }
            }
            return _value;
        }
    }
}

class BCLDoubleChecked : IHasValue
{
    private object _syncRoot = new object();
    private ValueToAccess _value;
    private bool _initialized;

    // 使用 LazyInitializer.EnsureInitialized 方法初始化
    // 该方法内部实现了双重锁定模式
    public ValueToAccess Value => LazyInitializer.EnsureInitialized(ref _value, ref _initialized, ref _syncRoot, Compute);
}

class BCLThreadSafeFactory : IHasValue
{
    private ValueToAccess _value;

    // 使用 LazyInitializer.EnsureInitialized 方法初始化
    // 这个构造函数的重载没有使用锁,会导致初始化方法 Compute 被执行多次,但是结果的对象仍然是线程安全的
    public ValueToAccess Value => LazyInitializer.EnsureInitialized(ref _value, Compute);
}

class LazyWrapper : IHasValue
{
    // 使用 Lazy<T>类型
    // 效果同使用 LazyInitializer 一样
    // 区别是 LazyInitializer 是静态类,不需要初始化
    private readonly Lazy<ValueToAccess> _value;

    public LazyWrapper(Lazy<ValueToAccess> value)
    {
        _value = value;
    }

    public ValueToAccess Value => _value.Value;
}

static ValueToAccess Compute()
{
    Console.WriteLine($"The value is being constructed on a thread id {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(TimeSpan.FromSeconds(1));
    return new ValueToAccess($"Constructed on thread id {Thread.CurrentThread.ManagedThreadId}");
}

interface IHasValue
{
    ValueToAccess Value { get; }
}

class ValueToAccess
{
    private readonly string _text;
    public ValueToAccess(string text)
    {
        _text = text;
    }
    public string Text => _text;
}

运行结果

txt
Worker runs on thread id 3
Worker runs on thread id 6
Worker runs on thread id 4
Worker runs on thread id 5
The value is being constructed on a thread id 3
The value is being constructed on a thread id 4
The value is being constructed on a thread id 5
The value is being constructed on a thread id 6
State value: Constructed on thread id 3
State value: Constructed on thread id 6
State value: Constructed on thread id 5
State value: Constructed on thread id 4
------------------------------
Worker runs on thread id 4
Worker runs on thread id 6
Worker runs on thread id 3
The value is being constructed on a thread id 4
Worker runs on thread id 5
State value: Constructed on thread id 4
State value: Constructed on thread id 4
State value: Constructed on thread id 4
State value: Constructed on thread id 4
------------------------------
Worker runs on thread id 5
Worker runs on thread id 4
Worker runs on thread id 6
Worker runs on thread id 7
The value is being constructed on a thread id 5
State value: Constructed on thread id 5
State value: Constructed on thread id 5
State value: Constructed on thread id 5
State value: Constructed on thread id 5
------------------------------
Worker runs on thread id 6
Worker runs on thread id 7
Worker runs on thread id 4
Worker runs on thread id 3
The value is being constructed on a thread id 6
State value: Constructed on thread id 6
State value: Constructed on thread id 6
State value: Constructed on thread id 6
State value: Constructed on thread id 6
------------------------------
Worker runs on thread id 7
Worker runs on thread id 6
Worker runs on thread id 3
The value is being constructed on a thread id 7
The value is being constructed on a thread id 3
Worker runs on thread id 4
The value is being constructed on a thread id 6
The value is being constructed on a thread id 4
State value: Constructed on thread id 7
State value: Constructed on thread id 7
State value: Constructed on thread id 7
State value: Constructed on thread id 7
------------------------------
Press Enter to exit