[英]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.