最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#多線程之線程同步

 更新時間:2022年03月22日 09:38:15   作者:.NET開發(fā)菜鳥  
這篇文章介紹了C#多線程之線程同步,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

一、前言

我們先來看下面一個例子:

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();
        }
    }
}

我們猜想一下程序的輸出結(jié)果是多少?2000?我們運行程序看一下輸出結(jié)果:

我們看到,程序最后輸出的結(jié)果跟我們預(yù)測的完全不一樣,這是什么原因呢?這就是由線程同步引起的問題。

線程同步問題:是解決多個線程同時操作一個資源的問題。

在上面的例子中,t1和t2兩個線程里面都是讓變量Counter的值自增1,假設(shè)這時t1線程讀取到Counter的值為200,可能t2線程執(zhí)行非??欤瑃1線程讀取Counter值的時候,t2線程已經(jīng)把Counter的值改為了205,等t1線程執(zhí)行完畢以后,Counter的值又被變?yōu)榱?01,這樣就會出現(xiàn)線程同步的問題了。那么該如何解決這個問題呢?

二、解決線程同步問題

1、lock

解決線程同步問題最簡單的是使用lock。lock可以解決多個線程同時操作一個資源引起的問題。lock是C#中的關(guān)鍵字,它要鎖定一個資源,lock的特點是:同一時刻只能有一個線程進(jìn)入lock的對象的范圍,其它lock的線程都要等待。我們看下面優(yōu)化后的代碼:

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();
        }
    }
}

這時我們在運行程序,查看輸出結(jié)果:

這時輸出結(jié)果是正確的。

注意:lock只能鎖住同一個對象,如果是不同的對象,還是會有線程同步的問題。lock鎖定的對象必須是引用類型的對象。

我們在定義一個Object類型的對象,lock分別鎖住兩個對象,看看是什么結(jié)果:

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();
        }
    }
}

程序運行結(jié)果:

可以看到,這時還是會有線程同步的問題。雖然使用了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();
        }
    }
}

我們看一下輸出結(jié)果:

可以看到,最終的余額并不是80,這也是線程同步帶來的問題,如何解決。解決思路就是使用同步的技術(shù)避免兩個線程同時修改一個余額。

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

在方法上面使用[MethodImpl(MethodImplOptions.Synchronized)],標(biāo)記該方法是同步方法,這樣一個方法只能同時被一個線程訪問。我們在QuQian的方法上面標(biāo)記,修改后的代碼如下:

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

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

        /// <summary>
        /// 定義一個取錢的方法,在上面標(biāo)記為同步方法
        /// </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();
        }
    }
}

程序輸出結(jié)果:

現(xiàn)在的方法就是“線程安全”的了。什么是“線程安全”呢?“線程安全”是指方法可以被多個線程隨意調(diào)用,而不會出現(xiàn)混亂。如果出現(xiàn)了混亂,那么就是“線程不安全”的。“線程安全”的方法可以在多線程里面隨意的使用。

3、對象互斥鎖

對象互斥鎖就是我們上面講的lock。我們在用lock來修改上面QuQian的例子:

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

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

        /// <summary>
        /// 定義一個取錢的方法,在上面標(biāo)記為同步方法
        /// </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();
        }
    }
}

程序輸出結(jié)果:

可以看到,最終的輸出結(jié)果還是80。

同一時刻只能有一個線程進(jìn)入同一個對象的lock代碼塊。必須是同一個對象才能起到互斥的作用。lock后必須是引用類型,不一定是object,只要是對象就行。

鎖對象選擇很重要,選不對就起不到同步的作用;選不對還有可能會造成其他地方被鎖,比如用字符串做鎖(因為字符串緩沖池導(dǎo)致導(dǎo)致可能用的是其他地方正在使用的鎖),所以不建議使用字符串做鎖。下面的代碼就是不允許的:

lock("locker")

兩個方法如果都用一個對象做鎖,那么訪問A的時候就不能訪問B,因此鎖選擇很重要。

4、Monitor

其實lock關(guān)鍵字就是對Monitor的簡化調(diào)用,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對象,就鎖定它,然后繼續(xù)執(zhí)行
            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();
        }
    }
}

程序輸出結(jié)果:

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對象,就鎖定它,然后繼續(xù)執(zhí)行
            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();
        }
    }
}

程序輸出結(jié)果:

到此這篇關(guān)于C#多線程之線程同步的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Unity3D實現(xiàn)模型隨機切割

    Unity3D實現(xiàn)模型隨機切割

    這篇文章主要為大家詳細(xì)介紹了Unity3D實現(xiàn)模型隨機切割,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • C#關(guān)閉指定名字進(jìn)程的方法

    C#關(guān)閉指定名字進(jìn)程的方法

    這篇文章主要介紹了C#關(guān)閉指定名字進(jìn)程的方法,涉及C#進(jìn)程操作的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • C#特性(Attribute)

    C#特性(Attribute)

    這篇文章介紹了C#的特性(Attribute),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • C# 設(shè)計模式系列教程-橋接模式

    C# 設(shè)計模式系列教程-橋接模式

    橋接模式降低了沿著兩個或多個維度擴展時的復(fù)雜度,防止類的過度膨脹,解除了兩個或多個維度之間的耦合,使它們沿著各自方向變化而不互相影響。
    2016-06-06
  • C#畫圖之餅圖折線圖的實現(xiàn)方法

    C#畫圖之餅圖折線圖的實現(xiàn)方法

    這篇文章主要介紹了C#畫圖之餅圖折線圖的實現(xiàn)方法,以實例形式講述了C#畫圖的完整實現(xiàn)過程,是非常實用的技巧,有不錯的借鑒價值,需要的朋友可以參考下
    2014-09-09
  • 基于C#實現(xiàn)一個最簡單的HTTP服務(wù)器實例

    基于C#實現(xiàn)一個最簡單的HTTP服務(wù)器實例

    這篇文章主要介紹了基于C#實現(xiàn)一個最簡單的HTTP服務(wù)器的方法,詳細(xì)分析了http服務(wù)器的實現(xiàn)原理與相關(guān)技巧,以及對應(yīng)的注意事項,需要的朋友可以參考下
    2014-12-12
  • 詳解C#中的Async和Await用法

    詳解C#中的Async和Await用法

    這篇文章主要介紹了C#中的Async和Await用法,包括在C#5.0下一些新特性的影響,需要的朋友可以參考下
    2015-07-07
  • OpenXml讀寫Excel實例代碼

    OpenXml讀寫Excel實例代碼

    這篇文章主要介紹了OpenXml讀寫Excel代碼分享,大家參考使用
    2013-12-12
  • 詳解C#中的字符串拼接@ $

    詳解C#中的字符串拼接@ $

    這篇文章主要介紹了C#中的字符串拼接@,$的相關(guān)知識,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • C# Winform中如何繪制動畫示例詳解

    C# Winform中如何繪制動畫示例詳解

    這篇文章主要給大家介紹了關(guān)于C# Winform中如何繪制動畫的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用C# Winform具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05

最新評論

万州区| 新建县| 静乐县| 苍南县| 玉门市| 泊头市| 河津市| 顺义区| 顺义区| 章丘市| 无锡市| 涞水县| 正宁县| 平潭县| 汽车| 华坪县| 石泉县| 蚌埠市| 新营市| 濉溪县| 花垣县| 保定市| 灵石县| 新野县| 加查县| 襄汾县| 房产| 合水县| 阳曲县| 临朐县| 元谋县| 乡宁县| 清涧县| 巴里| 汝南县| 阿瓦提县| 松滋市| 离岛区| 河西区| 福清市| 永胜县|