日记(2018-11-07)

2018-11-07日记

概览

今日立冬, 信息时代带来的焦躁让学习无法深入, 所以打算以写日记的形式戒掉焦躁, 重拾醉心学习的状态.

  • Synchronized与SyncRoot技术同步线程数据
  • Serializable特性作用
  • RPC(远程方法调用)

数据同步

在多个线程中共享数据, 很容易出现争用条件死锁

争用条件(RaceCondition)

设想如下代码和步骤:

  1. 第一个线程程序执行到if语句, 假设此时_state等于5, 条件为真
  2. 进入if语句之后, 它就被其他线程抢占, 调度器运行另一个线程
  3. 在另一个线程中某些代码将_state的值改变为6
  4. 第一个线程再次被调度, 此时_state等于6, 在执行完_state++语句后, _state将等于7
  5. 提示: 实际上 _state++的操作将从内存中获取值,给该值增加1, 然后再写回内存. 这些操作都可能被线程调度器打断, 造成线程不安全!
public void ChangeState()
{
    if (_state == 5)
    {
        _state++;
    }
}

解决方法是给_state对象加锁, 将锁定对象设置为线程安全对象, 一个线程锁住了_state对象, 其他线程就必须等待该锁定解除.

但是_state值对象, 不是引用对象, lock只能锁住引用对象, 因为锁住一个值的副本毫无意义. 那么就需要用一个对象来同步.代码如下:

public class StateObject
{
    private int _state = 5;
    private object sync = new object();
    
    public void ChangeState()
    {
        lock (sync)
        {
            if (_state == 5)
            {
                _state++;
            }
        }

    }
}

死锁(Deadlock)

过多的锁会导致线程都再等待对方解除锁定, 出现死锁. 所以再程序设计一开始就需要考虑到死锁问题, 设计好锁定顺序和锁定超时时间.

Synchronized与SyncRoot技术

再.net集合类型中, 比如HashtableArrayList都有Synchronized静态方法和SyncRoot实力方法. 返回一个线程安全的SyncHashtable对象, 这个对象中的方法, 比如Add都会锁定SyncRoot以确保线程安全的操作集合.

 namespace SynchronizationSamples
 {
     public class Demo
     {
         public virtual bool IsSynchronized => false;
         public virtual void DoThis() {}
         public virtual void DoThat() {}

         public static Demo Synchronized(Demo d)
         {
             if (!d.IsSynchronized)
             {
                 return new Synchronized(d);
             }
             return d;
         }
     }  
     
    // 同步版本
    private class SynchronizedDemo: Demo
    {
        public override bool IsSynchronized => true;
        private object _syncRoot = new object();
        private Demo _d;

        public SynchronizedDemo(Demo d)
        {
            _d = d;
        }

        public override void DoThis() 
        {
            lock (_syncRoot)
            {
                _d.DoThis();
            }
        }
        public override void DoThat() 
        {
            lock (_syncRoot)
            {
                _d.DoThat();
            }
        }

    }
     
 }

SyncRoot

确保再一个实例中, 不管再代码的任何位置调用, 返回的都是同一个对象, 它是唯一的.

public virtual object SyncRoot
{
    get
    {
        if (this._syncRoot == null)
        {
            // Interlocked为多个线程共享的变量提供原子操作, 原子操作就是单线程操作
            Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
        }
        return this._syncRoot;
    }
}

Serializable特性和RPC

todo: #1 今天太晚, 没时间再写了, 以后补上.

延申阅读

  • 并发集合类型

脚注

posted @ 2018-11-07 22:44 学友2000 阅读(...) 评论(...) 编辑 收藏