千家信息网

C#多线程中线程同步的示例分析

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章将为大家详细讲解有关C#多线程中线程同步的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、前言我们先来看下面一个例子:using System;u
千家信息网最后更新 2026年11月03日C#多线程中线程同步的示例分析

这篇文章将为大家详细讲解有关C#多线程中线程同步的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

一、前言

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

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;        ///         /// 定义一个取钱的方法        ///         ///         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;        ///         /// 定义一个取钱的方法,在上面标记为同步方法        ///         ///         [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;        ///         /// 定义一个取钱的方法,在上面标记为同步方法        ///         ///         //[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#多线程中线程同步的示例分析"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

线程 对象 同步 方法 问题 余额 结果 输出 取钱 程序 代码 完了 就是 安全 两个 例子 同时 标记 示例 C# 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 奇酷互联网科技 互联网金融科技创新 奇酷互联网络科技公司 互联网 科技服务平台 互联网科技生活的内容 北京三清互联网科技 银川新华互联网科技职业技术培训学校图片 洛阳昕诺飞互联网科技有限公司 电子科技大学互联网创业大赛 中国互联网十大科技股排名 本届互联网博览会上的黑科技 深圳艾果互联网通讯科技 长沙互联网科技 互联网金融科技公司的优势 电子商务与互联网科技的关系 广州百诚互联网科技有限公司官网 富国互联网科技002692 广西泛欧互联网科技有限公司 银川新华互联网科技学校招聘 小牛互联网科技有限公司地址 广州为想互联网科技有限公司 中国互联网科技工作委员会毛军 合肥互联网科技公司 隆华科技工业互联网 苏州铭阳互联网科技有限公司 互联网媒体平台算高科技产业 企晟互联网信息科技有限公司 美国互联网科技实力 全球科技与互联网公司市值变化 互联网科技创新方面的资料
0