【问题标题】:Global variable synchronization between threads in CC中线程之间的全局变量同步
【发布时间】:2016-07-08 20:45:37
【问题描述】:

我正在开发一个由 Arduino 控制的机器人。它将通过 TCP 从我的 PC 上的程序接收命令并通过串行通信转发到 Arduino。一旦 Arduino 执行命令,安装在其上的传感器会将环境状态返回给 PC。然后程序将运行一些算法来决定下一步要采取什么行动。我能够接收定向命令以从 Android 应用程序手动移动机器人。我使用 Raspberry Pi 作为消息传递/控制设备。

PC 上的程序、Android 应用程序和 Arduino 草图已完成并经过测试。但是在 Rpi 上运行的程序中的几个线程之间的通信问题仍然存在。

到目前为止,我有以下内容(TCP、串行、蓝牙套接字代码被省略,因为它们超出了这个问题的范围):

int canForwardToPC, recvFromPC, recvFromAndroid, recvFromAr, canSendCommand, isWaitingForInstruc, isWaitingForPos;

//all the int are initialized to be 0

void *ar_send(){
    int status;

    do{
        if(canSendCommand){
            status = write(ser, output, BUFFER_SIZE);
            if(status != -1) {

                printf("Sent to Arduino: %s\n", output);

                memset(&output[0], 0, sizeof(output));

                isWaitingForGridStr = 1;
                canSendCommand = 0;

                usleep(1000000);

            }else{
                ar_isConnected = 0;
                printf("Error Writing\n");
            }
        }
    }while(1);

}

void *tcp_send(){

    int status;

    do{
        if (canForwardToPC && tcp_isConnected){
            status = write(newsockfd, output, strlen(output));
            printf("Sent to PC: %s\n", output);
            memset(&output[0], 0, sizeof(output));
            if (status > 0) {

                isWaitingForInstruc = 1;
            }

            else{
                tcp_isConnected = 0;
                usleep(10000000);
            }
            canForwardToPC = 0;
        }
    } while (1);

}

void *bt_recv(){

    int bytes_read;

    do{
        bytes_read = read(client, bt_buffer, sizeof(bt_buffer));
        if(bytes_read > 0) {

            printf("Received \"%s\" from Android\n", bt_buffer);
            recvFromAndroid = 1;

        }
        else{

            bt_isConnected = 0;

        }
    }while(1);

}

void *ar_recv(){

    do{
        if(isWaitingForGridStr){

            n  = read(ser, ar_buffer, BUFFER_SIZE);

            if(n <= 0) continue;

            ar_buffer[BUFFER_SIZE] = '\0';

            printf("Received %s from Arduino.\n", ar_buffer);

            isWaitingForGridStr = 0;

            recvFromAr = 1;

        }
    }while(1);

}

void *tcp_recv(){

    do{
        if (tcp_isConnected && isWaitingForInstruc){


            n = read(newsockfd, tcp_buffer, BUFFER_SIZE);


            isWaitingForInstruc = 0;

            printf("Received %s from PC.\n", tcp_buffer);
            if (n > 0){
                recvFromPC = 1;

            }else{
                tcp_isConnected = 0;
                usleep(10000000);
            }
        }
    } while (1);

}

void *controller(){
do{
    if(recvFromAndroid){

        recvFromAndroid = 0;



        char temp[1];
        temp[0] = bt_buffer[0];

        canForwardToPC = 1;

        strncpy(output, temp, sizeof(temp));
        memset(&bt_buffer[0], 0, sizeof(bt_buffer));


    }

    else if(recvFromAr){

        recvFromAr = 0;


        canForwardToPC = 1;

        isWaitingForInstruc = 1;]

        strncpy(output, ar_buffer, sizeof(ar_buffer));
        memset(&ar_buffer[0], 0, sizeof(ar_buffer));

    }

    else if(recvFromPC){

        recvFromPC = 0;

        canSendCommand = 1;

        strncpy(output, tcp_buffer, sizeof(tcp_buffer));
        memset(&tcp_buffer[0], 0, sizeof(tcp_buffer));

    }

    }while(1);

}

每个方法都是一个线程,将在主函数中创建和加入。

据我所知,所有整数都很好地保护了输出,并且接收和发送线程组织得当。代码看起来是合法的。

但我有以下不按计划的部分输出:

Sent to PC: W1
Received W1 from PC.
Sent to Arduino: W1
Received 0:0:0:2:0:3 from Arduino.
Sent to PC: 0:0:0:2:0:3
Received W1W1W1W1W1D180W1A180W1 from PC.
Sent to Arduino: W1W1W1W1W1D180W1A180W1
Received -1:0:-1:2:0:3 from Arduino.
Received W1D180W1A180W1W1W1W1A180 from PC.
Sent to PC: -1:0:-1:2:0:3
Received W1 from PC.
Sent to Arduino: W1
Received -1:-1:-1:-1:0:3 from Arduino.
Received W1D180W1D180W1W1W1A180W1 from PC.
Sent to PC: -1:-1:-1:-1:0:3
Received W1 from PC.
Sent to Arduino: W1
Received -1:-1:-1:-1:0:3 from Arduino.
Received D180W1W1W1W1W1W1D180W1 from PC.
Sent to PC: D180W1W1W1W1W1W1D180W1
Received A180 from PC.
Sent to Arduino: A180
Received -1:-1:-1:1:0:3 from Arduino.
Sent to PC: -1:-1:-1:1:0:3
Received W1W1W1D180W1W1W1W1 from PC.
Received W1 from PC.

我唯一想发送给 PC 的是位置字符串,它的格式为 x:x:x:x:x:x。但是有一个实例是 D180W1W1W1W1W1W1D180W1 被发送到 PC。

预期的输出应该重复如下: 发送到电脑 从 PC 接收 发送到 Arduino 从 Arduino 接收

我怀疑发生了一些导致意外输出的全局变量不一致。

我找到了几篇关于信号量和互斥锁的文章,但这些只是针对 2 个线程的解决方案。我这里有 6 个线程在运行,我不知道如何实现信号量。

如何解决数据不一致问题?

任何帮助将不胜感激。

【问题讨论】:

  • 多线程中对共享对象的非同步、非只读、非原子访问是未定义的行为。
  • N个进程互斥?你可以实现en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm。但究竟为什么你需要它?也许更好的办法是重构你的代码。
  • 我不明白为什么你的程序中有 6 个线程。对于在 2 个设备之间进行通信的系统来说,这似乎是一个奇怪的设计选择,还是我在这里遗漏了什么?
  • @o_weisman 因为程序在 rpi 上运行。它基本上将消息从一个设备转发到另一个设备。每个线程处理不同的事情。 ar_send 处理发往 arduino 的传出消息; ar_recv 处理来自 arduino 的传入消息; tcp_send 处理传出到 pc 的消息; tcp_recv 处理来自 pc 的传入消息; bt_recv 处理来自 android 的传入消息。何时转发消息以及消息将转发到何处的所有逻辑都由控制器控制。有什么想法可以消除一些线程并使其更简单吗?

标签: c multithreading communication semaphore


【解决方案1】:

根据您告诉我的内容: 您应该为每个传入和传出通道实​​现不同的消息队列。每个通信线程只负责将传入的消息放入队列,或者从队列中取出消息并将它们发送到其通信端口。每个队列都有它的锁(互斥锁或其他)。主控制器(1 个线程)将有权访问所有队列,并将处理它们之间的消息移动。每个队列的互斥锁将被控制器或它的通信线程锁定。

流程示例:

  1. 消息由从 PC 读取的线程处理接收。
  2. 获取锁后将其放入来自 PC 的传入消息队列中,然后释放锁。
  3. 控制器唤醒(通过我推荐的定期调度,或通过某种事件机制)并尝试获取来自 PC 的传入消息队列的锁定。
  4. 它获取锁并读取那里的第一条消息。它发现它有一条消息给 arduino。
  5. 它接收消息,释放来自 PC 锁的传入。
  6. 获取传出到 arduino 锁,并将消息放入传出到 arduino 队列中。
  7. 发送到 arduino 线程唤醒,获取其队列锁,获取那里的所有消息并发送到 arduino。
  8. 发送到 arduino 线程释放锁并进入睡眠状态。

可以使所有传出线程定期或根据来自控制器的事件进行处理。 希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-10
    • 2018-07-20
    • 2017-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多