[英]How is it working with threads, pipes and interrupts in C with linux
它如何與linux(pi)一起使用線程,管道和中斷? 我創建了正在讀取功能中等待的工作程序rfm69_InterruptWorker
。 我對此線程設置了較高的優先級。
我想要/期望什么? 我希望當我向管道中寫入內容時,它將立即調用線程以從管道中讀取數據。 但是不幸的是,它將在主線程進入睡眠狀態時返回。
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <poll.h>
int pipe_test[2];
static struct pollfd gl_pfd;
pthread_t WorkerThread; // Thread to compute log messages
struct data* WorkerThreadRet; // Storage for thread return value
static void* rfm69_InterruptWorker(void* dummy)
{
uint8_t status;
uint8_t retval;
uint32_t timeout = 0;
struct sched_param params; // struct sched_param is used to store the scheduling priority
int ret;
if (pipe(pipe_test) < 0) {
printf("pipe error\r\n");
}
printf("created pipe \r\n");
gl_pfd.fd = pipe_test[0];
gl_pfd.events = POLLIN;
printf("setting prio\r\n");
// We'll set the priority to the minimum
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
// Attempt to set thread priority to the SCHED_OTHER policy
ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶ms);
printf("before poll/read\r\n");
//retval = poll(&gl_pfd, 1, -1);
//retval = poll(gl_pdf,2, -1); //When with polling gpio-pin and pipe to break
retval = read(pipe_test[0], &status, sizeof(status));
printf("after poll\r\n");
}
void main() {
uint8_t status;
pthread_create(&WorkerThread, NULL, rfm69_InterruptWorker, WorkerThreadRet);
usleep(1000);
printf("write to pipe\r\n");
write(pipe_test[1], &status, sizeof(status));
printf("Go to sleep\r\n");
usleep(1);
printf("back to sleep\r\n");
}
輸出:
created pipe
setting prio
before poll/read
write to pipe
Go to sleep
after poll
back to sleep
通常,您將使用條件變量或信號量來同步線程。 但是在這種情況下,只要通信方向保持不變, pipe
也可以幫助同步線程,否則您將需要2個管道。
首先,我將在創建工作線程之前創建管道。 這樣,主線程無需等待工作人員創建管道,它可以立即開始對其進行寫入。 我們可以使用此行為來同步線程,因為read
將阻塞,直到另一端在管道中寫入某些內容為止。 因此,工作線程實際上要等到管道中有數據為止。
我已經修改了您的示例,以證明這一點。 請注意,我還在主線程中使用了sleep
,但是我沒有使用它來同步線程,而是為了表明工作線程確實等到主線程寫了一些東西:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
typedef struct th_data {
int fd[2];
// add other arguments as needed
} th_data;
void *myworker(void *args)
{
int payload;
th_data *data = args;
time_t now;
time_t start, stop;
start = time(NULL);
ssize_t ret = read(data->fd[0], &payload, sizeof payload);
now = time(NULL);
if(ret == -1)
{
perror("read, terminating worker");
pthread_exit(0);
}
stop = time(NULL);
printf("* worker [%zu]: waited %zu seconds, payload: %d\n", now, stop-start, payload);
//////////////////
start = time(NULL);
ret = read(data->fd[0], &payload, sizeof payload);
now = time(NULL);
if(ret == -1)
{
perror("read, terminating worker");
pthread_exit(0);
}
stop = time(NULL);
printf("* worker [%zu]: waited %zu seconds, payload: %d\n", now, stop-start, payload);
//////////////////
start = time(NULL);
ret = read(data->fd[0], &payload, sizeof payload);
now = time(NULL);
if(ret == -1)
{
perror("read, terminating worker");
pthread_exit(0);
}
stop = time(NULL);
printf("* worker [%zu]: waited %zu seconds, payload: %d\n", now, stop-start, payload);
pthread_exit(0);
}
int main(void)
{
pthread_t th;
th_data data;
time_t now;
if(pipe(data.fd) < 0)
{
perror("pipe");
return 1;
}
pthread_create(&th, NULL, myworker, &data);
int payload = 88;
printf("+ Main thread: sleep 1 second\n");
sleep(1);
now = time(NULL);
printf("+ Main thread [%zu], writing...\n", now);
write(data.fd[1], &payload, sizeof payload);
printf("+ Main thread: sleep 2 seconds\n");
sleep(2);
payload = -12;
now = time(NULL);
printf("+ Main thread [%zu], writing...\n", now);
write(data.fd[1], &payload, sizeof payload);
printf("+ Main thread: sleep 3 seconds\n");
sleep(3);
payload = 1024;
now = time(NULL);
printf("+ Main thread [%zu], writing...\n", now);
write(data.fd[1], &payload, sizeof payload);
pthread_join(th, NULL);
return 0;
}
該程序的輸出是
+ Main thread: sleep 1 second
+ Main thread [1524698241], writing...
+ Main thread: sleep 2 seconds
* worker [1524698241]: waited 1 seconds, payload: 88
+ Main thread [1524698243], writing...
+ Main thread: sleep 3 seconds
* worker [1524698243]: waited 2 seconds, payload: -12
+ Main thread [1524698246], writing...
* worker [1524698246]: waited 3 seconds, payload: 1024
從方括號中的數字可以看出,工作線程在主線程向管道中寫入內容后立即恢復工作。 這些行有點不同步的事實是因為printf
被緩沖了。 但是方括號中的數字可以准確告訴您何時執行了線程的哪一部分。
老實說,我不太喜歡這種方法,如果主線程和工作人員都必須互相讀寫,那么一個管道是不夠的。 在這種情況下,您需要另一個管道來實現另一個方向,或者需要更好的同步策略。 我將使用條件變量,因為它們在多個線程中也能很好地工作。 具有條件變量的類似示例
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int payload; // shared resource
void *worker(void *args)
{
int i = 0;
int payloads[] = { 1, 3, -9};
size_t len = sizeof payloads / sizeof payloads[0];
int oldpayload;
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cv, &mutex);
printf("* worker[%d]: main thread signal cond variable, payload is %d\n", i, payload);
oldpayload = payload;
payload = payloads[i % len];
printf("* worker[%d]: setting payload to %d\n", i, payload);
pthread_mutex_unlock(&mutex);
// now waking up master
pthread_cond_signal(&cv);
if(oldpayload == 99)
break;
i++;
}
printf("* worker: My work is done, bye\n");
pthread_exit(0);
}
int main(void)
{
pthread_t th;
pthread_create(&th, NULL, worker, NULL);
int payloads[] = { 19, -12, 110, 1024, 99 };
for(size_t i = 0; i < sizeof payloads / sizeof payloads[0]; ++i)
{
printf("+ main[%zu]: doing some work, setting payload to %d\n", i, payloads[i]);
fflush(stdout);
usleep(10000); // simulation work
payload = payloads[i];
pthread_cond_signal(&cv);
// time for master to wait
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cv, &mutex);
printf("+ main[%zu]: worker set payload to %d\n\n", i, payload);
fflush(stdout);
pthread_mutex_unlock(&mutex);
}
pthread_join(th, NULL);
return 0;
}
的輸出是
+ main[0]: doing some work, setting payload to 19
* worker[0]: main thread signal cond variable, payload is 19
* worker[0]: setting payload to 1
+ main[0]: worker set payload to 1
+ main[1]: doing some work, setting payload to -12
* worker[1]: main thread signal cond variable, payload is -12
* worker[1]: setting payload to 3
+ main[1]: worker set payload to 3
+ main[2]: doing some work, setting payload to 110
* worker[2]: main thread signal cond variable, payload is 110
* worker[2]: setting payload to -9
+ main[2]: worker set payload to -9
+ main[3]: doing some work, setting payload to 1024
* worker[3]: main thread signal cond variable, payload is 1024
* worker[3]: setting payload to 1
+ main[3]: worker set payload to 1
+ main[4]: doing some work, setting payload to 99
* worker[4]: main thread signal cond variable, payload is 99
* worker[4]: setting payload to 3
* worker: My work is done, bye
+ main[4]: worker set payload to 3
這里要注意的一件事:變量payload
是我們的共享資源,它由主線程和工作線程讀取和寫入。 通常,您將需要一個互斥體來保護共享資源的讀寫。 請注意,寫入payload
時,主線程不會鎖定互斥體。 我之所以可以省略它的原因是由於condition變量:我同步了線程,以便一次只能有一個線程可以寫入payload
,所以我知道
payload = payloads[i];
在主線程上執行時,工作線程被鎖定並等待條件變量的信號,因此它不會與主線程同時寫入payload
。 當然,我可以為此使用另一個互斥體,但是我認為沒有必要。
最后說明:
定義main
的正確方法是以下三種方法之一:
int main(void);
int main(int argc, char **argv);
int main(int argc, char *argv[]);
還請記住,變量WorkerThreadRet
指向NULL
。 在將其傳遞給線程之前,您需要為其分配內存。
在你做代碼之前
write(pipe_test[1], &status, sizeof(status));
您可能要在寫操作之前初始化status
變量,否則將發送一些未初始化的值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.