服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - c# 进程内部的同步

c# 进程内部的同步

2022-10-13 13:25一只独行的猿 C#

这篇文章主要介绍了c# 进程内部的同步,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下

  在线程里,如果需要共享数据,那么一定需要使用同步技术,确保一次只有一个线程访问和改变共享数据的状态。在.net中,lock语句、Interlocked类和Monitor类可用于进程内部的同步。

1、lock语句与线程安全

  lock语句是设置锁定和解除锁定的一种简单方式。在使用lock语句之前,先进入另一个争用条件。例如:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class SharedState
{
  public int State { get; set; }
}
public class Job
{
  SharedState sharedState;
  public Job(SharedState sharedState)
  {
    this.sharedState = sharedState;
  }
  public void DoTheJob()
  {
    for (int i = 0; i < 50000; i++)
    {
        sharedState.State += 1;
    }
  }
}
static void Main()
{
  int numTasks = 20;
  var state = new SharedState();
  var tasks = new Task[numTasks];//定义20个任务
 
  for (int i = 0; i < numTasks; i++)
  {
    tasks[i] = Task.Run(() => new Job(state).DoTheJob());//启动20个任务,同时对数据进行修改
  }
 
  for (int i = 0; i < numTasks; i++)
  {
    tasks[i].Wait();//等待所有任务结束
  }
 
  Console.WriteLine("summarized {0}", state.State);//预想应该输出:summarized 1000000
}

  实际上的输出与预想输出并不一致,每次运行的输出结果都不同,但没有一个是正确的。如果将线程数量减少,那么得到正确值的次数会增多,但也不是每次都正确。

  使用lock关键字,可以实现多个线程访问同一个数据时的同步问题。lock语句表示等待指定对象的锁定,该对象只能时引用类型。进行锁定后——只锁定了一个线程,就运行lock语句块中的代码,在lock块最后接触锁定,以便另一个线程可以锁定该对象。

?
1
2
3
4
5
6
7
8
9
lock(obj)
{
  //执行代码
}
//锁定静态成员,可以所以其类型(object)
lock(typeof(StaticCalss))
{
  //执行代码
}

  所以修改以上的代码,使用SyncRoot模式。但是,如果是对属性的访问进行锁定:

?
1
2
3
4
5
6
7
8
9
10
11
public class SharedState
{
  private object syncRoot = new object();
 
  private int state = 0;
  public int State
  {
    get { lock (syncRoot) return state; }
    set { lock (syncRoot) state = value; }
  }
}

  仍会出现前面的争用情况。在方法调用get存储器,以获得state的当前值,然后set存储器给state设置新值。在调用对象的get和set存储器期间,对象并没有锁定,另一个线程仍然可以获得临时值。最好的方法是在不改变SharedState类的前提下,在调用方法中,将lock语句添加到合适的地方:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SharedState
{
  public int State { get; set; }
}
public class Job
{
  SharedState sharedState;
  public Job(SharedState sharedState)
  {
    this.sharedState = sharedState;
  }
  public void DoTheJob()
  {
    for (int i = 0; i < 50000; i++)
    {
      lock (sharedState)
      {
        sharedState.State += 1;
      }
    }
  }
}

  在一个地方使用lock语句并不意味着访问对象的其他线程都在等待。必须对每个访问共享数据的线程显示使用同步功能。

  为使对state的修改作为一个原子操作,修改代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SharedState
{
  private int state = 0;
  public int State { get { return state; } }
  public int IncrementState()
  {
    lock (this)
    {
      return ++state;
    }
  }
}
//外部访问
public void DoTheJob()
{
  for (int i = 0; i < 50000; i++)
  {
     sharedState.IncrementState();   
  }
}

2、Interlocked类

  Interlocked类用于使变量的简单语句原子化。i++并非线程安全的,它涉及三个步骤:取值、自增、存值。这些操作可能被线程调度器打断。Interlocked类提供了以线程安全的方式递增、递减、交换和读取值的方法。Interlocked类只能用于简单的同步问题,而且很快。因此,上面的IncrementState()方法的代码可以改为:return Interlocked.Increment(ref state);

3、Monitor类

  lcok语句最终会有C#编译器解析为使用Monitor类。

?
1
2
3
4
lock(obj)
{
  //执行代码
}

  简单的lock(obj)语句会被解析为调用Enter()方法,该方法会一直等待,直到线程锁定对象。一次只有一个线程能锁定对象,只要解除锁定,线程就可以进入同步阶段。Monitor类的Exit()方法解除锁定。编译器把Exit()方法放在try块的finally中,不论是否抛出异常,都将在语句块运行末尾解除锁定。

?
1
2
3
4
5
6
7
8
9
Monitor.Enter(obj);
try
{
  //执行代码
}
finally
{
  Monitor.Exit(obj);
}

  相对于lock语句,Mpnitor类可以设置一个等待被锁定的超时值。这样就不会无限期的等待锁定,如果等待锁定时间超过规定时间,则返回false,表示未被锁定,线程不再等待,执行其他操作。也许以后,该线程会再次尝试获得锁定:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool lockTaken = false;
Monitor.TryEnter(obj,500, ref lockTaken);//在500ms内,是否锁定了对象
if (lockTaken)
{
  try
  {
    //执行代码
  }
  finally
  {
    Monitor.Exit(obj);
  }
}
else
{
  //未获得锁定,执行代码
}

   如果基于对象的锁定对象(Monitor)的系统开销由于垃圾回收而过高,可以使用SpinLock结构。,SpinLock结构适用于:有大量的锁定,而且锁定时间总是非常短的情况。应避免使用多个SpinLock结构,也不要调用任何可能阻塞的内容。

以上就是c# 进程内部的同步的详细内容,更多关于c# 进程同步的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/pilgrim/p/9249981.html

延伸 · 阅读

精彩推荐
  • C#WinForm天猫双11自动抢红包源码分享

    WinForm天猫双11自动抢红包源码分享

    这篇文章主要为大家分享了WinForm天猫双11自动抢红包源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    polk611112021-12-08
  • C#基于C#动手实现网络服务器Web Server

    基于C#动手实现网络服务器Web Server

    这篇文章主要为大家详细介绍了基于C#动手实现网络服务器Web Server,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Deali-Axy5462022-01-25
  • C#C#保存与读取DataTable信息到XML格式的方法

    C#保存与读取DataTable信息到XML格式的方法

    这篇文章主要介绍了C#保存与读取DataTable信息到XML格式的方法,实例分析了C#读取DataTable信息到XML格式及读取XML格式数据到DataTable的相关技巧,具有一定参考借...

    北风其凉8852021-10-19
  • C#浅谈C# winForm 窗体闪烁的问题

    浅谈C# winForm 窗体闪烁的问题

    下面小编就为大家带来一篇浅谈C# winForm 窗体闪烁的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网8102021-12-21
  • C#C#多线程爬虫抓取免费代理IP的示例代码

    C#多线程爬虫抓取免费代理IP的示例代码

    本篇文章主要介绍了C#多线程爬虫抓取免费代理IP的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    L-H5682022-01-20
  • C#C# List 排序各种用法与比较

    C# List 排序各种用法与比较

    这篇文章主要介绍了C# List 排序各种用法与比较的相关资料,需要的朋友可以参考下...

    alun-chen11992021-12-07
  • C#C#环形缓冲区(队列)完全实现

    C#环形缓冲区(队列)完全实现

    这篇文章主要为大家详细介绍了C#环形缓冲区(队列)完全实现代码,感兴趣的小伙伴们可以参考一下...

    番茄炒西红柿12082021-12-02
  • C#解析C#设计模式编程中的装饰者模式

    解析C#设计模式编程中的装饰者模式

    这篇文章主要介绍了C#设计模式编程中的装饰者模式,同时也谈到了其在.NET框架中的应用,需要的朋友可以参考下...

    张龙豪3742021-11-12