国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C#多線程學習(三) 生產(chǎn)者和消費者

這篇具有很好參考價值的文章主要介紹了C#多線程學習(三) 生產(chǎn)者和消費者。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

C#多線程學習(三) 生產(chǎn)者和消費者

線程學習第一篇:C#多線程學習(一) 多線程的相關概念
線程學習第二篇:C#多線程學習(二) 如何操縱一個線程

前面說過,每個線程都有自己的資源,但是代碼區(qū)是共享的,即每個線程都可以執(zhí)行相同的函數(shù)。這可能帶來的問題就是幾個線程同時執(zhí)行一個函數(shù),導致數(shù)據(jù)的混亂,產(chǎn)生不可預料的結(jié)果,因此我們必須避免這種情況的發(fā)生。

C#提供了一個關鍵字lock,它可以把一段代碼定義為互斥段(critical section),互斥段在一個時刻內(nèi)只允許一個線程進入執(zhí)行,而其他線程必須等待。在C#中,關鍵字lock定義如下:

lock(expression) statement_block

expression代表你希望跟蹤的對象,通常是對象引用。
如果你想保護一個類的實例,一般地,你可以使用this;
如果你想保護一個靜態(tài)變量(如互斥代碼段在一個靜態(tài)方法內(nèi)部),一般使用類名就可以了。

statement_block就是互斥段的代碼,這段代碼在一個時刻內(nèi)只可能被一個線程執(zhí)行。

下面是一個使用lock關鍵字的典型例子,在注釋里說明了lock關鍵字的用法和用途。

示例如下:

using System;
using System.Threading;
namespace ThreadSimple
{
    internal class Account
    {
        int balance;
        Random r = new Random();
        internal Account(int initial)
        {
            balance = initial;
        }
        internal int Withdraw(int amount)
        {
            if (balance < 0)
            {
                //如果balance小于 0 則拋出異常
                throw new Exception("Negative Balance");
            }
            //下面的代碼保證在當前線程修改balance的值完成之前
            //不會有其他線程也執(zhí)行這段代碼來修改balance的值
            //因此,balance的值是不可能小于0的
            lock (this)
            {
                Console.WriteLine("Current Thread:"+Thread.CurrentThread.Name);
                //如果沒有l(wèi)ock關鍵字的保護,那么可能在執(zhí)行完if的條件判斷之后
                //另外一個線程卻執(zhí)行了balance=balance-amount修改了balance的值
                //而這個修改對這個線程是不可見的,所以可能導致這時if的條件已經(jīng)不成立了
                //但是,這個線程卻繼續(xù)執(zhí)行balance=balance-amount,所以導致balance可能小于0
                if (balance >= amount)
                {
                    Thread.Sleep(5);
                    balance = balance - amount;
                    return amount;
                }
                else
                {
                    return 0;// transaction rejected
                }
            }
        }
        internal void DoTransactions()
        {
            for (int i = 0; i < 100; i++)
            Withdraw(r.Next(-50,100));
        }
    }
    internal class Test
    {
        static internal Thread[] threads = new Thread[10];
        public static void Main()
        {
            Account acc = new Account(0);
            for (int i = 0; i < 10; i++)
            {
                Thread t = new Thread(new ThreadStart(acc.DoTransactions));
                threads[i] = t;
            }
            for (int i = 0; i < 10; i++)
                threads[i].Name=i.ToString();
            for (int i = 0; i < 10; i++)
                threads[i].Start();
            Console.ReadLine();
        }
    }
}

Monitor 類鎖定一個對象

當多線程公用一個對象時,也會出現(xiàn)和公用代碼類似的問題,這種問題就不應該使用lock關鍵字了,這里需要用到System.Threading中的一個類Monitor,我們可以稱之為監(jiān)視器,Monitor提供了使線程共享資源的方案。

Monitor類可以鎖定一個對象,一個線程只有得到這把鎖才可以對該對象進行操作。對象鎖機制保證了在可能引起混亂的情況下一個時刻只有一個線程可以訪問這個對象。

Monitor必須和一個具體的對象相關聯(lián),但是由于它是一個靜態(tài)的類,所以不能使用它來定義對象,而且它的所有方法都是靜態(tài)的,不能使用對象來引用。下面代碼說明了使用Monitor鎖定一個對象的情形:

......
Queue oQueue = new Queue();
......
Monitor.Enter(oQueue);
......//現(xiàn)在oQueue對象只能被當前線程操縱了
Monitor.Exit(oQueue);//釋放鎖

如上所示,當一個線程調(diào)用Monitor.Enter()方法鎖定一個對象時,這個對象就歸它所有了,其它線程想要訪問這個對象,只有等待它使用Monitor.Exit()方法釋放鎖。為了保證線程最終都能釋放鎖,你可以把Monitor.Exit()方法寫在try-catch-finally結(jié)構中的finally代碼塊里。

對于任何一個被Monitor鎖定的對象,內(nèi)存中都保存著與它相關的一些信息:
其一是現(xiàn)在持有鎖的線程的引用;
其二是一個預備隊列,隊列中保存了已經(jīng)準備好獲取鎖的線程;
其三是一個等待隊列,隊列中保存著當前正在等待這個對象狀態(tài)改變的隊列的引用。

當擁有對象鎖的線程準備釋放鎖時,它使用Monitor.Pulse()方法通知等待隊列中的第一個線程,于是該線程被轉(zhuǎn)移到預備隊列中,當對象鎖被釋放時,在預備隊列中的線程可以立即獲得對象鎖。

下面是一個展示如何使用lock關鍵字和Monitor類來實現(xiàn)線程的同步和通訊的例子,也是一個典型的生產(chǎn)者與消費者問題。
這個例程中,生產(chǎn)者線程和消費者線程是交替進行的,生產(chǎn)者寫入一個數(shù),消費者立即讀取并且顯示(注釋中介紹了該程序的精要所在)。

用到的系統(tǒng)命名空間如下:

using System;
using System.Threading;

首先,定義一個被操作的對象的類Cell,在這個類里,有兩個方法:ReadFromCell()WriteToCell()。消費者線程將調(diào)用ReadFromCell()讀取cellContents的內(nèi)容并且顯示出來,生產(chǎn)者進程將調(diào)用WriteToCell()方法向cellContents寫入數(shù)據(jù)。

示例如下:

public class Cell
{
    int cellContents;//Cell對象里邊的內(nèi)容
    bool readerFlag = false;//狀態(tài)標志,為true時可以讀取,為false則正在寫入
    public int ReadFromCell()
    {
        lock(this)//Lock關鍵字保證了什么,請大家看前面對lock的介紹
        {
            if (!readerFlag)//如果現(xiàn)在不可讀取
            {
                try
                {
                    //等待WriteToCell方法中調(diào)用Monitor.Pulse()方法
                    Monitor.Wait(this);
                }
                catch (SynchronizationLockException e)
                {
                    Console.WriteLine(e);
                }
                catch (ThreadInterruptedException e)
                {
                    Console.WriteLine(e);
                }
            }
            Console.WriteLine("Consume: {0}",cellContents);
            readerFlag = false;
            //重置readerFlag標志,表示消費行為已經(jīng)完成
            Monitor.Pulse(this);
            //通知WriteToCell()方法(該方法在另外一個線程中執(zhí)行,等待中)
        }
        return cellContents;
    }

    public void WriteToCell(int n)
    {
        lock(this)
        {
            if (readerFlag)
            {
                try
                {
                    Monitor.Wait(this);
                }
                catch (SynchronizationLockException e)
                {
                    //當同步方法(指Monitor類除Enter之外的方法)在非同步的代碼區(qū)被調(diào)用
                    Console.WriteLine(e);
                }
                catch (ThreadInterruptedException e)
                {
                    //當線程在等待狀態(tài)的時候中止
                    Console.WriteLine(e);
                }
            }
            cellContents = n;
            Console.WriteLine("Produce: {0}",cellContents);
            readerFlag = true;
            Monitor.Pulse(this);
            //通知另外一個線程中正在等待的ReadFromCell()方法
        }
    }
}

下面定義生產(chǎn)者類 CellProd 和消費者類 CellCons ,它們都只有一個方法ThreadRun(),以便在Main()函數(shù)中提供給線程的ThreadStart代理對象,作為線程的入口。

public class CellProd
{
    Cell cell; //被操作的Cell對象
    int quantity = 1; //生產(chǎn)者生產(chǎn)次數(shù),初始化為1
    public CellProd(Cell box, int request)//構造函數(shù)
    {
        cell = box;
        quantity = request;
    }
    public void ThreadRun()
    {
        for(int looper = 1; looper<=quantity; looper++)
            cell.WriteToCell(looper); //生產(chǎn)者向操作對象寫入信息
    }
}
public class CellCons
{
    Cell cell;
    int quantity = 1;
    public CellCons(Cell box, int request)//構造函數(shù)
    {
        cell = box;
        quantity = request;
    }
    public void ThreadRun()
    {
        int valReturned;
        for(int looper = 1; looper<=quantity; looper++)
            valReturned=cell.ReadFromCell();//消費者從操作對象中讀取信息
    }
}

然后在下面這個類MonitorSample的Main()函數(shù)中,我們要做的就是創(chuàng)建兩個線程分別作為生產(chǎn)者和消費者,使用CellProd.ThreadRun()方法和CellCons.ThreadRun()方法對同一個Cell對象進行操作。

public class MonitorSample
{
    public static void Main(String[] args)
    {
        int result = 0;//一個標志位,如果是0表示程序沒有出錯,如果是1表明有錯誤發(fā)生
        Cell cell = new Cell();
        //下面使用cell初始化CellProd和CellCons兩個類,生產(chǎn)和消費次數(shù)均為 20 次
        CellProd prod = new CellProd(cell, 20);
        CellCons cons = new CellCons(cell, 20);
        Thread producer = new Thread(new ThreadStart(prod.ThreadRun));
        Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));
        //生產(chǎn)者線程和消費者線程都已經(jīng)被創(chuàng)建,但是沒有開始執(zhí)行
        try
        {
            producer.Start();
            consumer.Start();
            producer.Join();
            consumer.Join();
            Console.ReadLine();
        }
        catch (ThreadStateException e)
        {
            //當線程因為所處狀態(tài)的原因而不能執(zhí)行被請求的操作
            Console.WriteLine(e);
            result = 1;
        }
        catch (ThreadInterruptedException e)
        {
            //當線程在等待狀態(tài)的時候中止
            Console.WriteLine(e);
            result = 1;
        }
        //盡管Main()函數(shù)沒有返回值,但下面這條語句可以向父進程返回執(zhí)行結(jié)果
        Environment.ExitCode = result;
    }
}

在上面的例程中,同步是通過等待Monitor.Pulse()來完成的。首先生產(chǎn)者生產(chǎn)了一個值,而同一時刻消費者處于等待狀態(tài),直到收到生產(chǎn)者的“脈沖(Pulse)”通知它生產(chǎn)已經(jīng)完成,此后消費者進入消費狀態(tài),而生產(chǎn)者開始等待消費者完成操作后將調(diào)用Monitor.Pulese()發(fā)出的“脈沖”。

它的執(zhí)行結(jié)果很簡單:

Produce: 1
Consume: 1
Produce: 2
Consume: 2
Produce: 3
Consume: 3
...
...
Produce: 20
Consume: 20

事實上,這個簡單的例子已經(jīng)幫助我們解決了多線程應用程序中可能出現(xiàn)的大問題,只要領悟了解決線程間沖突的基本方法,很容易把它應用到比較復雜的程序中去。文章來源地址http://www.zghlxwxcb.cn/news/detail-420105.html

到了這里,關于C#多線程學習(三) 生產(chǎn)者和消費者的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 線程同步--生產(chǎn)者消費者模型

    線程同步--生產(chǎn)者消費者模型

    條件變量是 線程間共享的全局變量 ,線程間可以通過條件變量進行同步控制 條件變量的使用必須依賴于互斥鎖以確保線程安全,線程申請了互斥鎖后,可以調(diào)用特定函數(shù) 進入條件變量等待隊列(同時釋放互斥鎖) ,其他線程則可以通過條件變量在特定的條件下喚醒該線程( 喚醒后線

    2024年01月19日
    瀏覽(25)
  • 多線程之生產(chǎn)者消費者

    目的是回顧多線程的幾個api 多生產(chǎn)者+多消費者+共享池

    2024年02月07日
    瀏覽(27)
  • 【Linux學習】多線程——信號量 | 基于環(huán)形隊列的生產(chǎn)者消費者模型 | 自旋鎖 | 讀寫鎖

    【Linux學習】多線程——信號量 | 基于環(huán)形隊列的生產(chǎn)者消費者模型 | 自旋鎖 | 讀寫鎖

    ??作者:一只大喵咪1201 ??專欄:《Linux學習》 ??格言: 你只管努力,剩下的交給時間! 之前在學習進程間通信的時候,本喵簡單的介紹過一下信號量,今天在這里進行詳細的介紹。 這是之前寫的基于阻塞隊列的生產(chǎn)者消費者模型中向阻塞隊列中push任務的代碼。 上面代碼

    2024年02月07日
    瀏覽(21)
  • 【Linux】線程安全-生產(chǎn)者消費者模型

    【Linux】線程安全-生產(chǎn)者消費者模型

    1個線程安全的隊列:只要保證先進先出特性的數(shù)據(jù)結(jié)構都可以稱為隊列 這個隊列要保證互斥(就是保證當前只有一個線程對隊列進行操作,其他線程不可以同時來操作),還要保證同步,當生產(chǎn)者將隊列中填充滿了之后要通知消費者來進行消費,消費者消費之后通知生產(chǎn)者

    2024年02月10日
    瀏覽(25)
  • 線程同步--生產(chǎn)者消費者模型--單例模式線程池

    線程同步--生產(chǎn)者消費者模型--單例模式線程池

    條件變量是 線程間共享的全局變量 ,線程間可以通過條件變量進行同步控制 條件變量的使用必須依賴于互斥鎖以確保線程安全,線程申請了互斥鎖后,可以調(diào)用特定函數(shù) 進入條件變量等待隊列(同時釋放互斥鎖) ,其他線程則可以通過條件變量在特定的條件下喚醒該線程( 喚醒后線

    2024年01月20日
    瀏覽(22)
  • python爬蟲,多線程與生產(chǎn)者消費者模式

    使用隊列完成生產(chǎn)者消費者模式 使用類創(chuàng)建多線程提高爬蟲速度 通過隊列可以讓線程之間進行通信 創(chuàng)建繼承Thread的類創(chuàng)建線程,run()會在線程start時執(zhí)行 吃cpu性能

    2024年02月09日
    瀏覽(18)
  • 線程池-手寫線程池C++11版本(生產(chǎn)者-消費者模型)

    線程池-手寫線程池C++11版本(生產(chǎn)者-消費者模型)

    本項目是基于C++11的線程池。使用了許多C++的新特性,包含不限于模板函數(shù)泛型編程、std::future、std::packaged_task、std::bind、std::forward完美轉(zhuǎn)發(fā)、std::make_shared智能指針、decltype類型推斷、std::unique_lock鎖等C++11新特性功能。 本項目有一定的上手難度。推薦參考系列文章 C++11實用技

    2024年02月13日
    瀏覽(26)
  • JavaEE 初階篇-生產(chǎn)者與消費者模型(線程通信)

    JavaEE 初階篇-生產(chǎn)者與消費者模型(線程通信)

    ??博客主頁:?【 小扳_-CSDN博客】 ?感謝大家點贊??收藏?評論? ? 文章目錄 ? ? ? ? 1.0 生產(chǎn)者與消費者模型概述 ? ? ? ? 2.0?在生產(chǎn)者與消費者模型中涉及的關鍵概念 ? ? ? ? 2.1 緩沖區(qū) ? ? ? ? 2.2 生產(chǎn)者 ? ? ? ? 2.3 消費者 ? ? ? ? 2.4 同步機制 ? ? ? ? 2.5 線程間通

    2024年04月28日
    瀏覽(31)
  • 探究:kafka生產(chǎn)者/消費者與多線程安全

    探究:kafka生產(chǎn)者/消費者與多線程安全

    目錄 1. 多線程安全 1.1. 生產(chǎn)者是多線程安全的么? 1.1. 消費者是多線程安全的么? 2. 消費者規(guī)避多線程安全方案 2.1. 每個線程維護一個kafkaConsumer 2.2. [單/多]kafkaConsumer實例 + 多worker線程 2.3.方案優(yōu)缺點對比 ????????Kafka生產(chǎn)者是 線程安全 的,可以在多個線程中共享一個

    2023年04月26日
    瀏覽(24)
  • 多線程(初階七:阻塞隊列和生產(chǎn)者消費者模型)

    多線程(初階七:阻塞隊列和生產(chǎn)者消費者模型)

    目錄 一、阻塞隊列的簡單介紹 二、生產(chǎn)者消費者模型 1、舉個栗子: 2、引入生產(chǎn)者消費者模型的意義: (1)解耦合 (2)削峰填谷 三、模擬實現(xiàn)阻塞隊列 1、阻塞隊列的簡單介紹 2、實現(xiàn)阻塞隊列 (1)實現(xiàn)普通隊列 (2)加上線程安全 (3)加上阻塞功能 3、運用阻塞隊列

    2024年02月05日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包