广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C#多线程之线程同步
  • 848
分享到

C#多线程之线程同步

2024-04-02 19:04:59 848人浏览 安东尼
摘要

一、前言 我们先来看下面一个例子: using System; using System.Threading; namespace ThreadSynchDemo { cl

一、前言

我们先来看下面一个例子:

using System;
using System.Threading;

namespace ThreadSynchDemo
{
    class Program
    {
        private static int Counter = 0;
        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 1000; i++)
                {
                    Counter++;
                    Thread.Sleep(1);
                }
            });
            t1.Start();

            Thread t2 = new Thread(() => {
                for (int i = 0; i < 1000; i++)
                {
                    Counter++;
                    Thread.Sleep(1);
                }
            });
            t2.Start();

            Thread.Sleep(3000);
            Console.WriteLine(Counter);
            Console.ReadKey();
        }
    }
}

我们猜想一下程序的输出结果是多少?2000?我们运行程序看一下输出结果:

我们看到,程序最后输出的结果跟我们预测的完全不一样,这是什么原因呢?这就是由线程同步引起的问题。

线程同步问题:是解决多个线程同时操作一个资源的问题

在上面的例子中,t1和t2两个线程里面都是让变量Counter的值自增1,假设这时t1线程读取到Counter的值为200,可能t2线程执行非常快,t1线程读取Counter值的时候,t2线程已经把Counter的值改为了205,等t1线程执行完毕以后,Counter的值又被变为了201,这样就会出现线程同步的问题了。那么该如何解决这个问题呢?

二、解决线程同步问题

1、lock

解决线程同步问题最简单的是使用lock。lock可以解决多个线程同时操作一个资源引起的问题。lock是C#中的关键字,它要定一个资源,lock的特点是:同一时刻只能有一个线程进入lock的对象的范围,其它lock的线程都要等待。我们看下面优化后的代码:

using System;
using System.Threading;

namespace ThreadSynchDemo
{
    class Program
    {
        private static int Counter = 0;
        // 定义一个locker对象
        private static Object locker = new Object();
        static void Main(string[] args)
        {
            #region 存在线程同步问题
            //Thread t1 = new Thread(() => {
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        Counter++;
            //        Thread.Sleep(1);
            //    }
            //});
            //t1.Start();

            //Thread t2 = new Thread(() => {
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        Counter++;
            //        Thread.Sleep(1);
            //    }
            //});
            //t2.Start(); 
            #endregion

            #region 使用Lock解决线程同步问题
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 1000; i++)
                {
                    lock(locker)
                    {
                        Counter++;
                    }
                    Thread.Sleep(1);
                }
            });
            t1.Start();

            Thread t2 = new Thread(() => {
                for (int i = 0; i < 1000; i++)
                {
                    lock (locker)
                    {
                        Counter++;
                    }
                    Thread.Sleep(1);
                }
            });
            t2.Start();
            #endregion

            Thread.Sleep(3000);
            Console.WriteLine(Counter);
            Console.ReadKey();
        }
    }
}

这时我们在运行程序,查看输出结果:

这时输出结果是正确的。

注意:lock只能锁住同一个对象,如果是不同的对象,还是会有线程同步的问题。lock锁定的对象必须是引用类型的对象。

我们在定义一个Object类型的对象,lock分别锁住两个对象,看看是什么结果:

using System;
using System.Threading;

namespace ThreadSynchDemo
{
    class Program
    {
        private static int Counter = 0;
        // 定义一个locker对象
        private static Object locker = new Object();
        // 定义locker2
        private static Object locker2 = new Object();
        static void Main(string[] args)
        {
            #region 存在线程同步问题
            //Thread t1 = new Thread(() => {
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        Counter++;
            //        Thread.Sleep(1);
            //    }
            //});
            //t1.Start();

            //Thread t2 = new Thread(() => {
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        Counter++;
            //        Thread.Sleep(1);
            //    }
            //});
            //t2.Start(); 
            #endregion

            #region 使用Lock解决线程同步问题
            //Thread t1 = new Thread(() => {
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        lock(locker)
            //        {
            //            Counter++;
            //        }
            //        Thread.Sleep(1);
            //    }
            //});
            //t1.Start();

            //Thread t2 = new Thread(() => {
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        lock (locker)
            //        {
            //            Counter++;
            //        }
            //        Thread.Sleep(1);
            //    }
            //});
            //t2.Start();
            #endregion

            #region 使用lock锁住不同的对象也会有线程同步问题
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 1000; i++)
                {
                    lock (locker)
                    {
                        Counter++;
                    }
                    Thread.Sleep(1);
                }
            });
            t1.Start();

            Thread t2 = new Thread(() => {
                for (int i = 0; i < 1000; i++)
                {
                    lock (locker2)
                    {
                        Counter++;
                    }
                    Thread.Sleep(1);
                }
            });
            t2.Start();
            #endregion
            Thread.Sleep(3000);
            Console.WriteLine(Counter);
            Console.ReadKey();
        }
    }
}

程序运行结果:

可以看到,这时还是会有线程同步的问题。虽然使用了lock,但是我们锁住的是不同的对象,这样也会有线程同步问题。lock必须锁住同一个对象才可以。

我们下面在来看一个多线程同步问题的例子:

using System;
using System.Threading;

namespace ThreadSynchDemo2
{
    class Program
    {
        static int Money = 100;

        /// <summary>
        /// 定义一个取钱的方法
        /// </summary>
        /// <param name="name"></param>
        static void QuQian(string name)
        {
            Console.WriteLine(name + "查看一下余额" + Money);
            int yue = Money - 1;
            Console.WriteLine(name + "取钱");
            Money = yue;
            Console.WriteLine(name + "取完了,剩" + Money);
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    QuQian("t2");
                }
            });
            Thread t2 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    QuQian("t2");
                }
            });
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.WriteLine("余额" + Money);
            Console.ReadKey();
        }
    }
}

我们看一下输出结果:

可以看到,最终的余额并不是80,这也是线程同步带来的问题,如何解决。解决思路就是使用同步的技术避免两个线程同时修改一个余额。

2、最大粒度——同步方法

在方法上面使用[MethodImpl(MethodImplOptions.Synchronized)],标记该方法是同步方法,这样一个方法只能同时被一个线程访问。我们在QuQian的方法上面标记,修改后的代码如下:

using System;
using System.Runtime.CompilerServices;
using System.Threading;

namespace ThreadSynchDemo2
{
    class Program
    {
        static int Money = 100;

        /// <summary>
        /// 定义一个取钱的方法,在上面标记为同步方法
        /// </summary>
        /// <param name="name"></param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        static void QuQian(string name)
        {
            Console.WriteLine(name + "查看一下余额" + Money);
            int yue = Money - 1;
            Console.WriteLine(name + "取钱");
            Money = yue;
            Console.WriteLine(name + "取完了,剩" + Money);
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    QuQian("t2");
                }
            });
            Thread t2 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    QuQian("t2");
                }
            });
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.WriteLine("余额" + Money);
            Console.ReadKey();
        }
    }
}

程序输出结果:

现在的方法就是“线程安全”的了。什么是“线程安全”呢?“线程安全”是指方法可以被多个线程随意调用,而不会出现混乱。如果出现了混乱,那么就是“线程不安全”的。“线程安全”的方法可以在多线程里面随意的使用。

3、对象互斥锁

对象互斥锁就是我们上面讲的lock。我们在用lock来修改上面QuQian的例子:

using System;
using System.Runtime.CompilerServices;
using System.Threading;

namespace ThreadSynchDemo2
{
    class Program
    {
        static int Money = 100;

        /// <summary>
        /// 定义一个取钱的方法,在上面标记为同步方法
        /// </summary>
        /// <param name="name"></param>
        //[MethodImpl(MethodImplOptions.Synchronized)]
        //static void QuQian(string name)
        //{
        //    Console.WriteLine(name + "查看一下余额" + Money);
        //    int yue = Money - 1;
        //    Console.WriteLine(name + "取钱");
        //    Money = yue;
        //    Console.WriteLine(name + "取完了,剩" + Money);
        //}

        private static object locker = new object();
        static void QuQian(string name)
        {
            Console.WriteLine(name + "查看一下余额" + Money);
            int yue = Money - 1;
            Console.WriteLine(name + "取钱");
            Money = yue;
            Console.WriteLine(name + "取完了,剩" + Money);
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    // 使用对象互斥锁
                    lock(locker)
                    {
                        QuQian("t1");
                    }
                }
            });
            Thread t2 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    lock (locker)
                    {
                        QuQian("t2");
                    }
                }
            });
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.WriteLine("余额" + Money);
            Console.ReadKey();
        }
    }
}

程序输出结果:

可以看到,最终的输出结果还是80。

同一时刻只能有一个线程进入同一个对象的lock代码块。必须是同一个对象才能起到互斥的作用。lock后必须是引用类型,不一定是object,只要是对象就行。

锁对象选择很重要,选不对就起不到同步的作用;选不对还有可能会造成其他地方被锁,比如用字符串做锁(因为字符串缓冲池导致导致可能用的是其他地方正在使用的锁),所以不建议使用字符串做锁。下面的代码就是不允许的:

lock("locker")

两个方法如果都用一个对象做锁,那么访问A的时候就不能访问B,因此锁选择很重要。

4、Monitor

其实lock关键字就是对Monitor的简化调用,lock最终会被编译成Monitor,因此一般不直接使用Monitor类,看下面代码:

using System;
using System.Threading;

namespace MonitorDemo
{
    class Program
    {
        static int Money = 100;
        private static object locker = new object();
        static void QuQian(string name)
        {
            // 等待没有人锁定locker对象,就锁定它,然后继续执行
            Monitor.Enter(locker);
            try
            {
                Console.WriteLine(name + "查看一下余额" + Money);
                int yue = Money - 1;
                Console.WriteLine(name + "取钱");
                Money = yue;
                Console.WriteLine(name + "取完了,剩" + Money);
            }
            finally
            {
                // 释放locker对象的锁
                Monitor.Exit(locker);
            }
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                        QuQian("t1");
                }
            });
            Thread t2 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                        QuQian("t2");
                }
            });
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.WriteLine("余额" + Money);
            Console.ReadKey();
        }
    }
}

程序输出结果:

Monitor类里面还有TryEnter方法,如果Enter的时候有人在占用锁,它不会等待,而是会返回false。看下面的示例代码:

using System;
using System.Threading;

namespace MonitorDemo
{
    class Program
    {
        static int Money = 100;
        private static object locker = new object();
        static void QuQian(string name)
        {
            // 等待没有人锁定locker对象,就锁定它,然后继续执行
            Monitor.Enter(locker);
            try
            {
                Console.WriteLine(name + "查看一下余额" + Money);
                int yue = Money - 1;
                Console.WriteLine(name + "取钱");
                Money = yue;
                Console.WriteLine(name + "取完了,剩" + Money);
            }
            finally
            {
                // 释放locker对象的锁
                Monitor.Exit(locker);
            }
        }

        static void F1(int i)
        {
            if (!Monitor.TryEnter(locker))
            {
                Console.WriteLine("有人在锁着呢");
                return;
            }
            Console.WriteLine(i);
            Monitor.Exit(locker);
        }


        static void Main(string[] args)
        {
            //Thread t1 = new Thread(() => {
            //    for (int i = 0; i < 10; i++)
            //    {
            //            QuQian("t1");
            //    }
            //});
            //Thread t2 = new Thread(() => {
            //    for (int i = 0; i < 10; i++)
            //    {
            //            QuQian("t2");
            //    }
            //});
            Thread t1 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    F1(i);
                }
            });
            Thread t2 = new Thread(() => {
                for (int i = 0; i < 10; i++)
                {
                    F1(i);
                }
            });

            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.WriteLine("余额" + Money);
            Console.ReadKey();
        }
    }
}

程序输出结果:

到此这篇关于C#多线程之线程同步的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: C#多线程之线程同步

本文链接: https://www.lsjlt.com/news/143383.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C#多线程之线程同步
    一、前言 我们先来看下面一个例子: using System; using System.Threading; namespace ThreadSynchDemo { cl...
    99+
    2022-11-13
  • C#多线程之线程同步WaitHandle
    一、引言 在前面的文章中,我们是使用“锁”的方式实现了线程间的通信,这种通信方式比较笨重。除了锁之外,.NET中还提供了一些线程间更自由通讯的工具,他们提供了...
    99+
    2022-11-13
  • Java多线程之线程同步
    volatile 先看个例子 class Test { // 定义一个全局变量 private boolean isRun = true; // 从主线程调...
    99+
    2022-11-12
  • C#多线程系列之进程同步Mutex类
    Mutex 中文为互斥,Mutex 类叫做互斥锁。它还可用于进程间同步的同步基元。 Mutex 跟 lock 相似,但是 Mutex 支持多个进程。Mutex 大约比 lock 慢 ...
    99+
    2022-11-13
  • C++ 如何实现多线程与线程同步
    目录CreateThread 实现多线程:beginthreadex 实现多线程:CreateMutex 互斥锁实现线程同步:通过临界区实现线程同步:Semaphore 基于信号实现...
    99+
    2022-11-12
  • 多线程之线程同步的方法(7种)
    1. 锁机制:使用锁对象对需要同步的代码块进行加锁,确保同一时刻只有一个线程可以执行该代码块。2. 互斥量:使用互斥量(Mutex)...
    99+
    2023-09-15
    多线程
  • Python的线程之线程同步
    目录线程同步threading.Lock获取同步锁总结在多线程程序中,它们互相独立打印的时间却是错乱的! 如下图,明明t-0 > t-1 > t-2 (按照线程创建时间早...
    99+
    2022-11-12
  • C#多线程之线程锁
    目录一、Mutex类二、Mutex的用途三、Semaphore信号量1、简介2、初始化3、WaitOne()和Release()四、Monitor类典型的生产者与消费者实例五、Loc...
    99+
    2022-11-13
  • C#多线程中线程同步的示例分析
    这篇文章将为大家详细讲解有关C#多线程中线程同步的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、前言我们先来看下面一个例子:using System;using Syste...
    99+
    2023-06-29
  • c#多线程之线程基础
    目录一、简介二、创建线程三、暂停线程四、线程等待五、终止线程六、检测线程状态七、线程优先级八、前台线程和后台线程九、向线程传递参数十、使用C# Lock 关键字十一、使用Monito...
    99+
    2022-11-13
  • C#多线程之线程池(ThreadPool)
    一、简介 前面介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程、启动线程。从性能上来讲,这样做并不理想(因为每使用一个线程...
    99+
    2022-11-13
  • java多线程之线程同步的方法有哪些
    Java中线程同步的方法有以下几种:1. synchronized关键字:使用synchronized关键字可以实现对代码块、方法或...
    99+
    2023-09-27
    java
  • python多线程之间的同步(一)
    引言:       线程之间经常需要协同工作,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直到该线程完成对数据的操作。这些技术包括临界区(Critical Section),互斥量(Mutex),信号量(Semapho...
    99+
    2023-01-31
    多线程 python
  • C#多线程之线程通讯(AutoResetEvent)
    一、简介 我们在线程编程的时候往往会涉及到线程的通信,通过信号的接受来进行线程是否阻塞的操作。AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访...
    99+
    2022-11-13
  • C#多线程系列之线程池
    目录线程池ThreadPool 常用属性和方法线程池说明和示例线程池线程数线程池线程数说明不支持的线程池异步委托任务取消功能计时器线程池 线程池全称为托管线程池,线程池受 .NET ...
    99+
    2022-11-13
  • C++详解多线程中的线程同步与互斥量
    目录线程同步互斥量线程同步 #include <stdio.h> #include <pthread.h> #include <unistd.h>...
    99+
    2022-11-13
  • Java多线程之同步工具类CyclicBarrier
    目录1 CyclicBarrier方法说明2 CyclicBarrier实例3 CyclicBarrier源码解析CyclicBarrier构造函数 await方法 nextGene...
    99+
    2022-11-12
  • Java多线程之同步工具类CountDownLatch
    目录1 CountDownLatch主要方法2 CountDownLatch使用例子3 CountDownLatch源码分析构造函数countDown方法countDown方法的内部...
    99+
    2022-11-12
  • Java多线程之同步工具类Exchanger
    目录1 Exchanger 介绍2 Exchanger 实例exchange等待超时 3 实现原理1 Exchanger 介绍 前面分别介绍了CyclicBarrier、CountD...
    99+
    2022-11-12
  • Java多线程之同步锁-lock详解
    目录一、题目描述二、解题思路三、代码详解一、题目描述 题目: 同步锁出现的目的就是为了解决多线程安全问题。 同步锁的几种方式 synchronized 1、同步代码块 2、同步方法 ...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作