chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

RT-Thread記錄(六、IPC機制之信號量互斥量事件集)

矜辰所致 ? 來源:矜辰所致 ? 作者:矜辰所致 ? 2022-06-21 10:40 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

上文說到 RT-Thread 對臨界區(qū)的處理方式有多種,其中已經(jīng)分析了關閉調度器和屏蔽中斷的方式,
本文就來學學另外的線程同步方式。

目錄

前言
一、IPC機制
二、信號
2.1 信號量控制塊
2.2 信號量操作
2.2.1 創(chuàng)建和刪除
2.2.2 初始化和脫離
2.2.3 獲取信號量
2.2.4 釋放信號量
2.2.5 信號量控制
2.3 示例(典型停車場模型)
三、互斥量
3.1 優(yōu)先級翻轉
3.2 優(yōu)先級繼承
3.3 互斥量控制塊
3.4 互斥量操作
3.2.1 創(chuàng)建和刪除
3.2.2 初始化和脫離
3.2.3 獲取互斥量
3.2.4 釋放互斥量
3.5 示例(優(yōu)先級繼承)
四、事件集
4.1 事件集控制塊
4.2 事件集操作
4.2.1 創(chuàng)建和刪除
4.2.2 初始化和脫離
4.2.3 發(fā)送事件
4.2.4 接收事件
4.3 示例(邏輯與和邏輯或)
結語

前言

在我們專欄前面的文章中,已經(jīng)學習過 RT-Thread 線程操作函數(shù)、軟件定時器、臨界區(qū)的保護,我們都進行了一些底層的分析,能讓我們更加理解 RT-Thread 的內(nèi)核,但是也不要忽略了上層的函數(shù)使用 要理解 RT-Thread 面向對象的思想,對所有的這些線程啊,定時器,包括要介紹的信號量,郵箱這些,都是以 對象 來操作,直白的說來就是 對于所有這些對象,都是以結構體的形式來表示,然后通過對這個對象結構體的操作來進行的。


本文所要介紹的內(nèi)容屬于 IPC機制,這些內(nèi)容相對來說比較簡單,我們重點在于學會如何使用以及了解他們的使用場合。

本 RT-Thread 專欄記錄的開發(fā)環(huán)境:
RT-Thread記錄(一、版本開發(fā)環(huán)境及配合CubeMX) + http://www.brongaenegriffin.com/d/1850333.html
RT-Thread記錄(二、RT-Thread內(nèi)核啟動流程)+ http://www.brongaenegriffin.com/d/1850347.html
RT-Thread 內(nèi)核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread線程操作函數(shù))+ http://www.brongaenegriffin.com/d/1850351.html
RT-Thread記錄(四、RTT時鐘節(jié)拍和軟件定時器)+ http://www.brongaenegriffin.com/d/1850554.html
RT-Thread記錄(五、RT-Thread 臨界區(qū)保護) + http://www.brongaenegriffin.com/d/1850712.html


一、IPC機制

嵌入式操作系統(tǒng)中,運行代碼主要包括線程 和 ISR,在他們的運行過程中,因為應用或者多線程模型帶來的需求,有時候需要同步,有時候需要互斥,有時候也需要彼此交換數(shù)據(jù)。操作系統(tǒng)必須提供相應的機制來完成這些功能,這些機制統(tǒng)稱為 線程間通信(IPC機制)。

本文所要介紹的就是關于線程同步的信號量、互斥量、事件 也屬于 IPC機制。

RT-Thread 中的 IPC機制包括信號量、互斥量、事件、郵箱、消息隊列。對于學習 RT-Thread ,這些IPC機制我們必須要學會靈活的使用。

為什么要說一下這個IPC機制?

我們前面說到過,RT-Thread 面向對象的思想,所有的這些 IPC 機制都被當成一個對象,都有一個結構體控制塊,我們用信號量結構體來看一看:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

Kernel object有哪些,我們可以從基礎內(nèi)核對象結構體定義下面的代碼找到:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_10,color_FFFFFF,t_70,g_se,x_16

本節(jié)說明了 RT-Thread 的 IPC 機制,同時通過 信號量的結構體控制塊再一次的認識了 RT-Thread 面向對象的設計思想。

在我的 FreeRTOS 專欄中,對于FreeRTOS 的信號量,互斥量,事件集做過說明和測試。在這個部分,實際上 RT-Thread 與 FreeRTOS 是類似的,都是一樣的思想。所以如果屬熟悉FreeRTOS的話,這部分是簡單的,我們要做的就是記錄一下 對象的控制塊,和操作函數(shù),加以簡單的示例測試。

二、信號量

信號量官方的說明是:信號量是一種輕型的用于解決線程間同步問題的內(nèi)核對象,線程可以獲取或釋放它,從而達到同步或互斥的目的。

信號量非常靈活,可以使用的場合也很多:

  • 比如 一個典型的應用場合就是停車位模型,總共有多少個車位,就是多少個信號量,入口進入一輛車信號量-1,出口離開一輛車信號量+1。
  • 比如 兩個線程之間的同步,信號量的值初始化成 0,而嘗試獲得該信號量的線程,一定需要等待另一個釋放信號量的線程先執(zhí)行完。

在 FreeRTOS 中存在二值信號量,但是 RT-Thread 中已經(jīng)沒有了,官方有說明:

在這里插入圖片描述

信號量記住一句話基本就可以,釋放一次信號量就+1,獲取一次就-1,如果信號量數(shù)據(jù)為0,那么嘗試獲取的線程就會掛機,直到有線程釋放信號量使得信號量大于0。

2.1 信號量控制塊

老規(guī)矩用源碼,解釋看注釋(使用起來也方便復制 ~ ~?。?/p>

#ifdef RT_USING_SEMAPHORE
/**
 * Semaphore structure
 * value 信號量的值,直接表明目前信號量的數(shù)量
 */
struct rt_semaphore
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_uint16_t          value;                         /**< value of semaphore. */
    rt_uint16_t          reserved;                      /**< reserved field */
};
/*
rt_sem_t 是指向 semaphore 結構體的指針類型
*/
typedef struct rt_semaphore *rt_sem_t;
#endif

2.2 信號量操作

2.2.1 創(chuàng)建和刪除

同以前的線程那些一樣,動態(tài)的方式,先定義一個信號量結構體的指針變量,接收創(chuàng)建好的句柄。

創(chuàng)建信號量:

/*
參數(shù)的含義:
1、name 	信號量名稱
2、value 	信號量初始值
3、flag 	信號量標志,它可以取如下數(shù)值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
信號量創(chuàng)建成功,返回信號量的控制塊指針
信號量創(chuàng)建失敗,返回RT_BULL 
*/
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)

對于最后的參數(shù) flag,決定了當信號量不可用時(就是當信號量為0的時候),多個線程等待的排隊方式。只有RT_IPC_FLAG_FIFO (先進先出)或 RT_IPC_FLAG_PRIO(優(yōu)先級等待)兩種 flag。

關于用哪一個,要看具體的情況,官方有特意說明:

poYBAGKxL4KAYSAVAABtsu0TwBo813.png

刪除信號量:

/*
參數(shù):
sem 	rt_sem_create() 創(chuàng)建的信號量對象,信號量句柄
返回值:
RT_EOK 	刪除成功
*/
rt_err_t rt_sem_delete(rt_sem_t sem)

2.2.2 初始化和脫離

靜態(tài)的方式,先定義一個信號量結構體,然后對他進行初始化。

初始化信號量:

/**
參數(shù)的含義:
1、sem 		信號量對象的句柄,就是開始定義的信號量結構體變量
2、name 	信號量名稱
3、value 	信號量初始值
4、flag 	信號量標志,它可以取如下數(shù)值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_sem_init(rt_sem_t    sem,
                     const char *name,
                     rt_uint32_t value,
                     rt_uint8_t  flag)

脫離信號量:

/*
參數(shù):
sem 	信號量對象的句柄
返回值:
RT_EOK 	脫離成功
*/
rt_err_t rt_sem_detach(rt_sem_t sem);

2.2.3 獲取信號量

當信號量值大于零時,線程將獲得信號量,并且相應的信號量值會減 1。

/**
參數(shù):
1、sem 		信號量對象的句柄
2、time 	指定的等待時間,單位是操作系統(tǒng)時鐘節(jié)拍(OS Tick)
返回值:
RT_EOK 			成功獲得信號量
-RT_ETIMEOUT 	超時依然未獲得信號量
-RT_ERROR 		其他錯誤
 */
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)

注意!要等待的時間是系統(tǒng)時鐘節(jié)拍(OS Tick)。

無等待獲取信號量

//就是上面獲取的等待時間為0的方式
rt_err_t rt_sem_trytake(rt_sem_t sem)
{
    return rt_sem_take(sem, 0);
}

當線程申請的信號量資源實例為0時,直接返回 - RT_ETIMEOUT。

2.2.4 釋放信號量

釋放信號量可以使得該信號量+1,如果有線程在等待這個信號量,可以喚醒這個線程。

/**
參數(shù):
sem 	信號量對象的句柄
返回值:
RT_EOK 	成功釋放信號量
 */
rt_err_t rt_sem_release(rt_sem_t sem)

2.2.5 信號量控制

信號量控制函數(shù),用來重置信號量,使得信號量恢復為設定的值:


/**
 * This function can get or set some extra attributions of a semaphore object.
參數(shù):
sem 	信號量對象的句柄
cmd    信號量控制命令 ,支持命令:RT_IPC_CMD_RESET 
arg    暫時不知道
返回值:
RT_EOK 	成功釋放信號量

 */
rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg)
{
    rt_ubase_t level;

    /* parameter check */
    RT_ASSERT(sem != RT_NULL);
    RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);

    if (cmd == RT_IPC_CMD_RESET)
    {
        rt_ubase_t value;

        /* get value */
        value = (rt_ubase_t)arg;
        /* disable interrupt */
        level = rt_hw_interrupt_disable();

        /* resume all waiting thread */
        rt_ipc_list_resume_all(&sem->parent.suspend_thread);

        /* set new value */
        sem->value = (rt_uint16_t)value;

        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        rt_schedule();

        return RT_EOK;
    }

    return -RT_ERROR;
}

使用示例:

rt_err_t result;
rt_uint32_t value;

value = 10; /* 重置的值,即重置為10 */
result = rt_sem_control(sem, RT_IPC_CMD_RESET, (void*)value)

/* 重置為0 */
rt_sem_control(sem, RT_IPC_CMD_RESET, RT_NULL)

對sem重置后,會先把sem上掛起的所有任務進行喚醒(任務的error是-RT_ERROR),然后把sem的值會重新初始化成設定的值。

在官方論壇有如下說明:
在rt_sem_release后使用rt_sem_control的目的是因為在某些應用中必須rt_sem_take和rt_sem_release依次出現(xiàn),而不允許rt_sem_release被連續(xù)多次調用,一旦出現(xiàn)這種情況會被認為是出現(xiàn)了異常,通過調用rt_sem_control接口來重新初始化 sem_ack恢復異常。

2.3 示例(典型停車場模型)

前面說到過,信號量非常靈活,可以使用的場合也很多,官方也有很多例子,我們這里做個典型的示例
— 停車場模型(前面用截圖做解釋,后面會附帶源碼)。

示例中,我們使用兩個不同的按鍵來模擬車輛的進出,但是考慮到我們還沒有學設備和驅動,沒有添加按鍵驅動,所以我們用古老的方式來實現(xiàn)按鍵操作:

按鍵key3,代表車輛離開:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

按鍵key2,代表車輛進入:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

信號量的創(chuàng)建,初始10個車位:

pYYBAGKxL4OAAKKMAAAexk-g3H8704.png

當然不要忘了,車輛進入和車輛離開(兩個按鍵)是需要兩個線程的。

我們來看看測試效果,說明如圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_8,color_FFFFFF,t_70,g_se,x_16

注意上圖測試最后的細節(jié),雖然 one car get out! 但是打印出來的停車位還是0,可以這么理解,key3_thread_entry線程釋放了信號量以后還沒來得及打印,等待信號量的線程key2_thread_entry就獲取到了信號量。

具體的分析需要看rt_sem_release函數(shù)源碼,里面會判斷是否需要值+1,以及是否需要調度:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

附上上面測試代碼:

/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-02-16     RT-Thread    first version
 */

#include 
#include "main.h"
#include "usart.h"
#include "gpio.h"

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include 

static struct rt_thread led1_thread;    //led1線程
static char led1_thread_stack[256];

static rt_thread_t led2_thread = RT_NULL; //led2線程

static rt_thread_t key2_thread = RT_NULL; //

static rt_thread_t key3_thread = RT_NULL; //

rt_sem_t mysem;


static void led1_thread_entry(void *par){
    while(1){
        LED1_ON;
        rt_thread_mdelay(1000);
        LED1_OFF;
        rt_thread_mdelay(1000);
    }
}

static void led2_thread_entry(void *par){
    while(1){
        LED2_ON;
        rt_thread_mdelay(500);
        LED2_OFF;
        rt_thread_mdelay(500);
    }
}

static void key2_thread_entry(void *par){
    static rt_err_t result;
    while(1){
        if(key2_read == 0){
            rt_thread_mdelay(10); //去抖動
            if(key2_read == 0){
                result = rt_sem_take(mysem, 1000);
                if (result != RT_EOK)
                {
                    rt_kprintf("the is no parking spaces now...\r\n");
                }
                else
                {
                    rt_kprintf("one car get in!,we have %d parking spaces now...\r\n",mysem->value);
                }
                while(key2_read == 0){rt_thread_mdelay(10);}
            }
        }
        rt_thread_mdelay(1);
    }
}

static void key3_thread_entry(void *par){
    while(1){
        if(key3_read == 0){
            rt_thread_mdelay(10); //去抖動
            if(key3_read == 0){
                if(mysem->value < 10){
                    rt_sem_release(mysem);
                    rt_kprintf("one car get out!,we have %d parking spaces now...\r\n",mysem->value);
                }
                while(key3_read == 0){rt_thread_mdelay(10);} //去抖動
            }
        }
        rt_thread_mdelay(1);
    }
}
int main(void)
{
    MX_GPIO_Init();
    MX_USART1_UART_Init();


    rt_err_t rst2;
    rst2 = rt_thread_init(&led1_thread,
                        "led1_blink ",
                        led1_thread_entry,
                        RT_NULL,
                        &led1_thread_stack[0],
                        sizeof(led1_thread_stack),
                        RT_THREAD_PRIORITY_MAX -1,
                        50);

    if(rst2 == RT_EOK){
        rt_thread_startup(&led1_thread);
    }


    mysem = rt_sem_create("my_sem1", 10, RT_IPC_FLAG_FIFO);
    if(RT_NULL == mysem){
        LOG_E("create sem failed!...\n");
    }
    else LOG_D("we have 10 parking spaces now...\n");

    key2_thread = rt_thread_create("key2_control",
                                key2_thread_entry,
                                RT_NULL,
                                512,
                                RT_THREAD_PRIORITY_MAX -2,
                                50);

        /* 如果獲得線程控制塊,啟動這個線程 */
        if (key2_thread != RT_NULL)
            rt_thread_startup(key2_thread);

     key3_thread = rt_thread_create("key3_control",
                                key3_thread_entry,
                                RT_NULL,
                                512,
                                RT_THREAD_PRIORITY_MAX -2,
                                50);

        /* 如果獲得線程控制塊,啟動這個線程 */
        if (key3_thread != RT_NULL)
            rt_thread_startup(key3_thread);
    return RT_EOK;
}


void led2_Blink(){
    led2_thread = rt_thread_create("led2_blink",
                            led2_thread_entry,
                            RT_NULL,
                            256,
                            RT_THREAD_PRIORITY_MAX -1,
                            50);

    /* 如果獲得線程控制塊,啟動這個線程 */
    if (led2_thread != RT_NULL)
        rt_thread_startup(led2_thread);
}

MSH_CMD_EXPORT(led2_Blink, Led2 sample);

三、互斥量

互斥量是一種特殊的二值信號量。互斥量的狀態(tài)只有兩種,開鎖或閉鎖(兩種狀態(tài)值)。

互斥量支持遞歸,持有該互斥量的線程也能夠再次獲得這個鎖而不被掛起。自己能夠再次獲得互斥量。

互斥量可以解決優(yōu)先級翻轉問題,它能夠實現(xiàn)優(yōu)先級繼承。

互斥量互斥量不能在中斷服務例程中使用。

3.1 優(yōu)先級翻轉

優(yōu)先級翻轉,我以前寫過:

poYBAGKxL4SAUwRIAABs3UaqWig058.png

再用官方的圖加深理解:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

3.2 優(yōu)先級繼承

優(yōu)先級繼承,以前也寫過:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

再用官方的圖加深理解:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

需要切記的是互斥量不能在中斷服務例程中使用。

3.3 互斥量控制塊

#ifdef RT_USING_MUTEX
/**
 * Mutual exclusion (mutex) structure
 * parent 				繼承ipc類
 * value 				互斥量的值
 * original_priority 	持有線程的原始優(yōu)先級
 * hold 				持有線程的持有次數(shù),可以多次獲得
 * *owner				當前擁有互斥量的線程
 */
struct rt_mutex
{
    struct rt_ipc_object parent;              /**< inherit from ipc_object */
    rt_uint16_t          value;                /**< value of mutex */
    rt_uint8_t           original_priority;    /**< priority of last thread hold the mutex */
    rt_uint8_t           hold;                 /**< numbers of thread hold the mutex */

    struct rt_thread    *owner;               /**< current owner of mutex */
};
/* rt_mutext_t 為指向互斥量結構體的指針類型  */
typedef struct rt_mutex *rt_mutex_t;
#endif

3.4 互斥量操作

3.4.1 創(chuàng)建和刪除

先定義一個指向互斥量結構體的指針變量,接收創(chuàng)建好的句柄。

創(chuàng)建互斥量:

/**
參數(shù)的含義:
1、name 	互斥量名稱
2、flag 	該標志已經(jīng)作廢,無論用戶選擇 RT_IPC_FLAG_PRIO 還是 RT_IPC_FLAG_FIFO,
			內(nèi)核均按照 RT_IPC_FLAG_PRIO 處理
返回值:
互斥量創(chuàng)建成功,返回互斥量的控制塊指針
互斥量創(chuàng)建失敗,返回RT_BULL 
 */
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)

刪除互斥量:

/**
參數(shù):
mutex	互斥量對象的句柄
返回值:
RT_EOK 	刪除成
 */
rt_err_t rt_mutex_delete(rt_mutex_t mutex)

3.4.2 初始化和脫離

靜態(tài)的方式,先定義一個互斥量結構體,然后對他進行初始化。

初始化互斥量:

/**
參數(shù)的含義:
1、mutex 互斥量對象的句柄,指向互斥量對象的內(nèi)存塊,開始定義的結構體
2、name 	互斥量名稱
3、flag 	該標志已經(jīng)作廢,按照 RT_IPC_FLAG_PRIO (優(yōu)先級)處理
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)

脫離互斥量:

/**
參數(shù):
mutex	互斥量對象的句柄
返回值:
RT_EOK 	成功
 */
rt_err_t rt_mutex_detach(rt_mutex_t mutex)

3.4.3 獲取互斥量

一個時刻一個互斥量只能被一個線程持有。

如果互斥量沒有被其他線程控制,那么申請該互斥量的線程將成功獲得該互斥量。如果互斥量已經(jīng)被當前線程線程控制,則該互斥量的持有計數(shù)加 1,當前線程也不會掛起等待。

/**
參數(shù):
1、mutex	互斥量對象的句柄
2、time 	指定的等待時間,單位是操作系統(tǒng)時鐘節(jié)拍(OS Tick)
返回值:
RT_EOK 			成功獲得互斥量
-RT_ETIMEOUT 	超時依然未獲得互斥量
-RT_ERROR 		獲取失敗
 */
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)

3.4.4 釋放互斥量

在獲得互斥量后,應該盡可能的快釋放互斥量。

/**
參數(shù):
mutex 	互斥量對象的句
返回值:
RT_EOK 	成功
 */
rt_err_t rt_mutex_release(rt_mutex_t mutex)

3.5 示例(優(yōu)先級繼承)

互斥量做一個簡單的示例,但是即便簡單,也能體現(xiàn)出優(yōu)先級繼承這個機制。

示例中,我們使用兩個按鍵,key2按鍵,按一次獲取互斥量,再按一次釋放互斥量,打印自己初始優(yōu)先級,當前優(yōu)先級,互斥量占有線程優(yōu)先級這幾個量。key3按鍵,按一次,獲取互斥量,立馬就釋放,也打印幾個優(yōu)先級。

互斥量的創(chuàng)建,和兩個線程的優(yōu)先級:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16

key2操作:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

key3操作:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

測試結果說明圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

示例中為了更好的演示并沒有快進快出,實際使用還是需要快進快出,除非你自己就是有這種特出需求。

還有一個細節(jié),就是 RT-Thread 中對象的 名字,只能顯示8個字符長度,長了會截斷,并不影響使用。

四、事件集

事件集這部分與 FreeRTOS 基本一樣。

事件集主要用于線程間的同步,它的特點是可以實現(xiàn)一對多,多對多的同步。即一個線程與多個事件的關系可設置為:其中任意一個事件喚醒線程,或幾個事件都到達后才喚醒線程進行后續(xù)的處理;同樣,事件也可以是多個線程同步多個事件。

RT-Thread 定義的事件集有以下特點:

  • 事件只與線程相關,事件間相互獨立:每個線程可擁有 32 個事件標志,采用一個 32 bit 無符號整型數(shù)進行記錄,每一個 bit 代表一個事件;
  • 事件僅用于同步,不提供數(shù)據(jù)傳輸功能;
  • 事件無排隊性,即多次向線程發(fā)送同一事件 (如果線程還未來得及讀走),其效果等同于只發(fā)送一次。

4.1 事件集控制塊

#ifdef RT_USING_EVENT
/**
 * flag defintions in event
 * 邏輯與
 * 邏輯或
 * 清除標志位
 */
#define RT_EVENT_FLAG_AND               0x01            /**< logic and */
#define RT_EVENT_FLAG_OR                0x02            /**< logic or */
#define RT_EVENT_FLAG_CLEAR             0x04            /**< clear flag */

/*
 * event structure
 * set:事件集合,每一 bit 表示 1 個事件,bit 位的值可以標記某事件是否發(fā)生
 */
struct rt_event
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_uint32_t          set;                           /**< event set */
};
/* rt_event_t 是指向事件結構體的指針類型  */
typedef struct rt_event *rt_event_t;
#endif

4.2 事件集操作

4.2.1 創(chuàng)建和刪除

先定義一個指向事件集結構體的指針變量,接收創(chuàng)建好的句柄。

創(chuàng)建事件集:

/**
參數(shù)的含義:
1、name 	事件集的名稱
2、flag 	事件集的標志,它可以取如下數(shù)值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO理
返回值:
事件集創(chuàng)建成功,返回事件集的控制塊指針
事件集創(chuàng)建失敗,返回RT_BULL 
 */
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)

flag 使用哪一個,解釋和信號量一樣,可參考信號量創(chuàng)建部分說明。

刪除事件集:

/**
參數(shù):
event	事件集對象的句柄
返回值:
RT_EOK 	成功
 */
rt_err_t rt_event_delete(rt_event_t event)

4.2.2 初始化和脫離

靜態(tài)的方式,先定義一個事件集結構體,然后對他進行初始化。

初始化事件集:

/**
參數(shù)的含義:
1、event	事件集對象的句柄
2、name 	事件集的名稱
3、flag 	事件集的標志,它可以取如下數(shù)值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)

脫離事件集:

/**
參數(shù):
event	事件集對象的句柄
返回值:
RT_EOK 	成功
 */
rt_err_t rt_event_detach(rt_event_t event)

4.2.3 發(fā)送事件

發(fā)送事件函數(shù)可以發(fā)送事件集中的一個或多個事件。

/**
參數(shù)的含義:
1、event	事件集對象的句柄
2、set		發(fā)送的一個或多個事件的標志值
返回值:
RT_EOK 	成功
 */
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)

4.2.4 接收事件

內(nèi)核使用 32 位的無符號整數(shù)來標識事件集,它的每一位代表一個事件,因此一個事件集對象可同時等待接收 32 個事件,內(nèi)核可以通過指定選擇參數(shù) “邏輯與” 或“邏輯或”來選擇如何激活線程。

/**
參數(shù)的含義:
1、event		事件集對象的句柄
2、set			接收線程感的事件
3、option 		接收選項,可取的值為
#define RT_EVENT_FLAG_AND               0x01       邏輯與    
#define RT_EVENT_FLAG_OR                0x02       邏輯或    
#define RT_EVENT_FLAG_CLEAR             0x04     選擇清除重置事件標志位       
4、timeout		指定超時時間
5、recved		指向接收到的事件,如果不在意,可以使用 NULL
返回值:
RT_EOK 			成功
-RT_ETIMEOUT 	超時
-RT_ERROR 		錯誤
 */
rt_err_t rt_event_recv(rt_event_t   event,
                       rt_uint32_t  set,
                       rt_uint8_t   option,
                       rt_int32_t   timeout,
                       rt_uint32_t *recved)

4.3 示例(邏輯與和邏輯或)

事件集通過示例可以很好的理解怎么使用,我們示例中,用按鈕發(fā)送事件,其他線程接收事件,進行對應的處理。

按鍵操作:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

線程邏輯或處理:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_19,color_FFFFFF,t_70,g_se,x_16

邏輯或測試結果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16

線程邏輯與處理:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_19,color_FFFFFF,t_70,g_se,x_16

邏輯與測試結果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16

結語

本文雖然只是介紹了信號量、互斥量和事件集這幾個比較簡單的線程同步操作,但是最終完成了后發(fā)現(xiàn)內(nèi)容還是很多的。

洋洋灑灑這么多字,最終看下來自己還是挺滿意的,希望我把該表述的都表達清楚了,希望大家多多提意見,讓博主能給大家?guī)砀玫奈恼隆?/p>

那么下一篇的 RT-Thread 記錄,就要來說說與線程通訊 有關的 郵箱、消息隊列和信號內(nèi)容了。

謝謝!

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • IPC
    IPC
    +關注

    關注

    3

    文章

    374

    瀏覽量

    54073
  • RT-Thread
    +關注

    關注

    32

    文章

    1498

    瀏覽量

    43547
  • 信號量
    +關注

    關注

    0

    文章

    53

    瀏覽量

    8709
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    RT-thread內(nèi)核互斥

    便于恢復。hold: 表示此互斥鎖被同一線程成功take的次數(shù),一般情況下一個線程只會take一次互斥鎖,但rt-thread也允許線程重復take同一線程,此時hold的值就用來做記錄
    發(fā)表于 03-06 17:23

    第15章 互斥信號量

    轉rtx操作系統(tǒng) 本章節(jié)開始講解RTX的另一個重要的資源共享機制---互斥信號量(Mutex,即Mutual Exclusion的縮寫)。注意,建議初學者學習完上個章節(jié)的信號量后再學習
    發(fā)表于 10-06 16:40

    RT-Thread信號量刪除后釋放信號量跟獲取信號量還是成功

    RT-Thread中創(chuàng)建了一個動態(tài)的信號量,運行10次這個線程后刪除這個動態(tài)信號量,但是問題是10次后他再次釋放信號量跟獲取信號量還是成功的
    發(fā)表于 01-15 05:04

    互斥源碼分析測試

    文章目錄互斥源碼分析測試參考資料:RTT官網(wǎng)文檔關鍵字:分析RT-Thread源碼、stm32、RTOS、互斥。
    發(fā)表于 08-24 06:01

    淺析RT-Thread中事件的工作機制

    RT-Thread 中的事件,也就是其他 RTOS 中的事件標志組。事件也是線程(任務)間同步的一種機制。前面介紹的兩種線程間同步的方式(信號量
    發(fā)表于 04-11 15:31

    【原創(chuàng)精選】RT-Thread征文精選技術文章合集

    RT-Thread記錄(五、RT-Thread 臨界區(qū)保護)RT-Thread記錄、
    發(fā)表于 07-26 14:56

    RT-Thread操作系統(tǒng)互斥的使用方法與場合介紹

    可以進入。互斥工作機制互斥信號量不同的是:擁有互斥
    發(fā)表于 08-03 11:26

    Rt-thread里面的mem.c函數(shù)保護lfree全局變量為什么用信號量

    Rt-thread 里面的mem.c函數(shù)保護lfree全局變量為什么用信號量而不是互斥信號量,用信號量保護全局變量不怕造成線程優(yōu)先級翻轉嗎
    發(fā)表于 08-08 10:43

    RT-Thread互斥優(yōu)先級問題求解

    RT Thread優(yōu)先級問題,官網(wǎng)視頻,互斥一節(jié),明明是線程2的優(yōu)先級比線程1高,但線程1會優(yōu)先運行,不知是有什么機理還是Bug?經(jīng)反復測試發(fā)現(xiàn),將線程優(yōu)先級配置到接近線程優(yōu)先級的最
    發(fā)表于 12-09 15:43

    信號量互斥在使用過程中會存在這樣的死鎖嗎?

    如果A線程已經(jīng)獲取了信號量互斥,但此時B線程打斷了A線程,信號量互斥沒有釋放,并且在B線
    發(fā)表于 01-10 15:37

    Linux IPC System V 信號量

    semctl() //刪除信號量 ftok()//獲取key值, key值是System V IPC的標識符,成功返回key,失敗返回-1設errno//同
    發(fā)表于 04-02 14:46 ?518次閱讀

    詳解互斥信號量的概念和運行

    1 、互 斥 信 號 1.1 互斥信號量的概念及其作用 互斥信號量的主要作用是對資源實現(xiàn)互斥
    的頭像 發(fā)表于 10-22 11:57 ?1.2w次閱讀
    詳解<b class='flag-5'>互斥</b><b class='flag-5'>信號量</b>的概念和運行

    Linux信號量(2):POSIX 信號量

    (Inter-Process Communication) 機制之一,3 種 IPC 機制源于 POSIX.1 的實時擴展。Single UNIX Specification 將 3 種機制
    的頭像 發(fā)表于 10-29 17:34 ?1080次閱讀

    Free RTOS的互斥信號量

    二進制信號量互斥非常相似,但確實有一些細微的區(qū)別。互斥體包含優(yōu)先級繼承機制,而二進制信號量
    的頭像 發(fā)表于 02-10 15:36 ?1818次閱讀
    Free RTOS的<b class='flag-5'>互斥</b><b class='flag-5'>信號量</b>

    使用Linux信號量實現(xiàn)互斥點燈

    信號量常用于控制對共享資源的訪問,有計數(shù)型信號量和二值信號量之分。初始化時信號量值大于1的,就是計數(shù)型信號量,計數(shù)型
    的頭像 發(fā)表于 04-13 15:12 ?1200次閱讀
    使用Linux<b class='flag-5'>信號量</b>實現(xiàn)<b class='flag-5'>互斥</b>點燈