2018-11-07日记
概览
今日立冬, 信息时代带来的焦躁让学习无法深入, 所以打算以写日记的形式戒掉焦躁, 重拾醉心学习的状态.
- Synchronized与SyncRoot技术同步线程数据
- Serializable特性作用
- RPC(远程方法调用)
数据同步
在多个线程中共享数据, 很容易出现
争用条件
和死锁
争用条件(RaceCondition)
设想如下代码和步骤:
- 第一个线程程序执行到
if
语句, 假设此时_state
等于5, 条件为真 - 进入
if
语句之后, 它就被其他线程抢占, 调度器运行另一个线程 - 在另一个线程中某些代码将
_state
的值改变为6
- 第一个线程再次被调度, 此时
_state
等于6
, 在执行完_state++
语句后,_state
将等于7 - 提示: 实际上
_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集合类型中, 比如
Hashtable
和ArrayList
都有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 今天太晚, 没时间再写了, 以后补上.
延申阅读
- 并发集合类型