我的目标:线程将等待(忙碌循环不睡眠),直到按下特定键(假设为0)。每个线程都有一个不同的键,它将触发该线程退出等待并继续执行等待之后的命令。
我已尝试以下方法来实现此目的:
使用conio.h和getch(),但这已经过时了,不再使用gcc了。资料来源:Why can't I find <conio.h> on Linux?
使用ncurses.h和getch()但这会在等待键盘按下时停止执行。 我使用的代码:http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/scanw.html#GETCHCLASS
我使用termios.h的当前实现:
int main:
//Keypress Event Handler
struct termios info;
tcgetattr(0, &info); /* get current terminal attirbutes; 0 is the file descriptor for stdin */
info.c_lflag &= ~ICANON; /* disable canonical mode */
info.c_cc[VMIN] = 1; /* wait until at least one keystroke available */
info.c_cc[VTIME] = 0; /* no timeout */
tcsetattr(0, TCSANOW, &info); /* set immediately */
线程调用的内部函数(抱歉缩进):
while(stop_wait != 1)
{
//printf("%d\n", temp->currentID);
ch = getchar();
if(ch < 0) {
if (ferror(stdin)) {
clearerr(stdin);
}
}
switch (ch)
{
case 48 :
if(temp->event == 0) stop_wait = 1;
break;
case 49 :
if(temp->event == 1) stop_wait = 1;
break;
case 50 :
if(temp->event == 2) stop_wait = 1;
break;
case 51 :
if(temp->event == 3) stop_wait = 1;
break;
case 52 :
if(temp->event == 4) stop_wait = 1;
break;
}
}
主要结束:
tcgetattr(0, &info);
info.c_lflag |= ICANON;
tcsetattr(0, TCSANOW, &info);
上面的代码与此处的代码非常相似:Implementing a KeyPress Event in C
然而,这不符合我想要的正确方法。我有一个输入文件,指定哪些键将触发stop_wait更改为1.线程1将通过按键盘上的1(ascii中的49)触发,线程2将通过按键盘上的2触发(ascii中的50) )。当前实现的问题是,如果没有触发1,则不会触发2。如下所示(Main()语句显示执行结束时忽略它所说的内容):
我可以就此问题获得任何建议/帮助吗?
答案 0 :(得分:2)
我在评论中提到的多线程方法,有一个单独的线程来获取和排队键,被设计为不丢弃键,并不是一件容易的事。它需要一些C技能和一些UNIX知识。我实施了一个运行的工作骨架,因此您可以看到所涉及的内容。
要对此进行测试,请将文件另存为dispatch.c
$ cc -o dispatch dispatch.c
$ ./dispatch
示例输出:
$ ./dispatch
关键&#39; a&#39;按下...
......线程T3拉出关键&#39; a&#39;从队列中 ......线程T1拉出键&#39; a&#39;从队列中 ......线程T2拉出键&#39; a&#39;从队列中 关键&#39; b&#39;按下...
......线程T2拔出键&#39; b&#39;从队列中 ......线程T1拔出键&#39; b&#39;从队列中 关键&#39; c&#39;按下...
......线程T3拉出关键&#39; c&#39;从队列中 ......线程T1拔出键&#39; c&#39;从队列中 关键&#39; d&#39;按下...
......线程T2拔出键&#39; d&#39;从队列中 ......线程T3拉出关键&#39; d&#39;从队列中 关键&#39; z&#39;按下...
...线程T2拉出键&#39; z&#39;从队列中 ......线程T1拉出键&#39; z&#39;从队列中 ......线程T3拉出关键&#39; z&#39;从队列中 功能
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <strings.h>
#include <string.h>
#include <termios.h>
#include <sys/types.h>
typedef struct keyQueue {
struct keyQueue *next;
char key;
} keyQueue_t;
typedef struct ThreadInfo {
pthread_t tid; /* thread id */
pthread_mutex_t kqmutex; /* protects key queue from race condition between threads */
keyQueue_t kqhead; /* input keys queued to this thread */
char *keys; /* keys this thread responds to */
char *name; /* name of this thread */
} threadInfo_t;
static struct termios origtc, newtc;
threadInfo_t threads[] = {
{ 0, PTHREAD_MUTEX_INITIALIZER, { NULL, '\0' }, "abcez", "Thread T1" },
{ 0, PTHREAD_MUTEX_INITIALIZER, { NULL, '\0' }, "abdfz", "Thread T2" },
{ 0, PTHREAD_MUTEX_INITIALIZER, { NULL, '\0' }, "acdgz", "Thread T3" }
};
void *service(void *arg) {
char key;
threadInfo_t *t = &threads[(int)arg]; // get pointer to thread
for(;;) {
pthread_mutex_lock(&t->kqmutex); // lock other threads out while we tamper
key = '\0'; // initialize key to NULL
if (t->kqhead.next != NULL) { // Anything queued up for us?
keyQueue_t *kq = t->kqhead.next; // if so get ptr to key pkt
key = kq->key; // fetch key from pkt
t->kqhead.next = kq->next; // Point to next key in queue (or NULL if no more queued up).
free(kq);
}
pthread_mutex_unlock(&t->kqmutex); // unlock key queue
if (key != '\0') { // if we got a key, log it
printf("... %s pulled key '%c' from queue\n", t->name, key);
}
// ⇓ usleep() probably more practical as 1-sec too long for most cases
sleep(1); // sleep so we don't loop too fast eating CPU
}
return NULL;
}
int main() {
/* Fire up threads */
for (long i = 0; i < sizeof (threads) / sizeof (threadInfo_t); i++) {
if (pthread_create(&threads[i].tid, NULL, service, (void *)i) < 0) {
perror("pthread_create()");
exit(-1);
}
}
tcgetattr(0, &origtc); // get orig tty settings
newtc = origtc; // copy them
newtc.c_lflag &= ~ICANON; // put in '1 key mode'
newtc.c_lflag &= ~ECHO; // turn off echo
for(;;) {
tcsetattr(0, TCSANOW, &newtc); // echo off 1-key read mode
char c = getchar(); // get single key immed.
tcsetattr(0, TCSANOW, &origtc); // settings back to normal
printf("Key '%c' pressed...\n", c); // show user what we got
for (int i = 0; i < sizeof (threads) / sizeof (threadInfo_t); i++) {
threadInfo_t *t = &threads[i]; // get shorthand ptr to thread
if (strchr(t->keys, c) != NULL) { // this thread listens for this key
pthread_mutex_lock(&t->kqmutex); // lock other threads out while we tamper
keyQueue_t *kq = calloc(sizeof (struct keyQueue), 1); // allocate pkt
kq->key = c; // stash key there
keyQueue_t *kptr = &t->kqhead; // get pointer to queue head
while(kptr->next != NULL) // find first empty slot
kptr = kptr->next;
kptr->next = kq; // enqueue key packet to thread
pthread_mutex_unlock(&t->kqmutex); // unlock key queue
}
}
}
}
此代码启动三个线程t1,t2,t3,每个线程都有一个&#39;密钥队列&#39;它们上面的结构,以及char *
字段keys
。 keys
是一个字符串,其中包含线程对&#39;感兴趣的字符(键)。
字符串中列出的键盘键在线程字符串中重复,因此在某些情况下,一个键可以被多个线程使用。例如,所有线程都会听取&#39; a&#39;和&#39; z&#39;,两个线程听&#39;另外两个线程&#39; c&#39;另一对线程感兴趣&#39; d&#39; ,最后&#39; e&#39; f&#39;和&#39; g&#39;只有一个线程可以监听。
主循环读取没有回声的键并立即捕获键(例如,用户不必返回)。当输入一个密钥时,它会遍历线程信息以找出哪些线程对按下的密钥感兴趣,并将密钥(在一个数据包中)排入相应的线程。
线程在它们自己的循环中,在循环之间休眠一秒钟。当他们醒来时,他们检查他们的队列,看看是否有任何键排队。如果他们从队列中取出它并说他们从队列中拔出了该键。
由于每个线程的轮询/工作循环延迟(例如在线程唤醒并检查各自的队列之前),您有时间在键盘上输入多个内容以排队直到线程,然后线程将以1秒的间隔一次一个地将它们排队。
在现实生活中,程序会使用更短的睡眠时间,但会在其中放置某些东西,以防止每个线程不必要地占用大量的CPU时间。
运行它并看到它的实际效果很有趣。
*注意:使用calloc()
代替malloc()
,因为与malloc()
不同,calloc()
初始化返回到所有0的内存。这是一个很好的技巧。