gpt4 book ai didi

c - C中同时独立执行两个函数

转载 作者:可可西里 更新时间:2023-11-01 11:51:13 25 4
gpt4 key购买 nike

我想我可能在 c 中遇到线程问题,但我不确定。

我的目标是在 while(1) 循环内执行两个独立的函数,如下所示:其中一个函数是 kbget() 以检索在非规范模式下在终端中按下的键。第二种是通过 ioctl(1, TIOCGWINSZ,...) 函数不断获取终端窗口大小。

它通常不起作用,因为 while(1) 循环在执行第二个函数以重新评估终端窗口大小之前停止以从用户那里获取按键。如果在按下某个键之前调整了终端窗口的大小,则除非再次按下随机键,否则不会执行评估其大小的函数。

换句话说,除非按下某个键,否则调整终端窗口的大小不会更新下面的 Window 结构中的大小值。我希望程序在调整终端大小时“实时”更新 y_size 和 x_size 值。

这是没有 POSIX 线程的代码中的问题:执行:

gcc -Wall scr.h main.c -o main && ./main 

(下面的scr.h有kbget()来改变终端模式):

主.c:

#include "scr.h"
#include <sys/ioctl.h>

#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x)) // equivalent to move(y, x) in ncurses
#define del_from_cursor(x) printf("\033[%dX", (x)) // delete x characters from cursor position

typedef struct {
int y_size;
int x_size;
} Window;

int main(void)
{
printf("\033[?1049h\033[2J\033[H"); // remember position & clear screen

gotoyx(1, 10);
printf("Press <ESC> to stop program.");
gotoyx(2, 10);
printf("Resizing the terminal window does not 'automatically' update the size shown on screen");

Window w;
struct winsize w_s;

while (1) {

// evaluate terminal size
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w.y_size = w_s.ws_row;
w.x_size = w_s.ws_col;
}

// print terminal size and center it
gotoyx(w.y_size / 2, w.x_size / 2);
del_from_cursor(5);
printf("w.y_size: %d", w.y_size);
gotoyx((w.y_size / 2) + 1, w.x_size / 2);
del_from_cursor(5);
printf("w.x_size: %d", w.x_size);

// get key pressed by user in terminal & exit if <ESC> is pressed
if (kbget() == 0x001b) { break; }
}
printf("\033[2J\033[H\033[?1049l"); // clear screen & restore
return 0;
}

我曾尝试使用线程解决此问题,但到目前为止我没有成功。

我通过添加 2 个函数(get_window_size 和 get_key)修改了上面的 main.c 文件:(scr.h在get_key()中有kbget()函数,可以把终端改成规范模式)

主.c:

#include "scr.h"
#include <sys/ioctl.h>
#include <pthread.h>

#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))

typedef struct {
int y_size;
int x_size;
} Window;

void *get_window_size(void *arg)
{
Window *w = (Window *)arg;
struct winsize w_s;
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w->y_size = w_s.ws_row;
w->x_size = w_s.ws_col;
}
pthread_exit(0);
}

void *get_key(void *arg)
{
int *key = (int *)arg;
free(arg);
*key = kbget();
int *entered_key = malloc(sizeof(*key));

*entered_key = *key;
pthread_exit(entered_key);
}

int main(void)
{
printf("\033[?1049h\033[2J\033[H");

Window w;

pthread_t tid[3];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&tid[0], &attr, get_window_size, &w);

int *c = malloc(sizeof(*c));
int *key_pressed;

while (1) {
// for initial size
pthread_join(tid[0], NULL);

// printing size to screen
gotoyx(w.y_size / 2, w.x_size / 2);
del_from_cursor(5);
printf("w.y_size: %d", w.y_size);
gotoyx((w.y_size / 2) + 1, w.x_size / 2);
del_from_cursor(5);
printf("w.x_size: %d", w.x_size);

// get window size
pthread_attr_t attr1;
pthread_attr_init(&attr1);
pthread_create(&tid[1], &attr1, get_window_size, &w);

// get key entered by user
pthread_attr_t attr2;
pthread_attr_init(&attr2);
pthread_create(&tid[2], &attr2, get_key, c);

pthread_join(tid[1], NULL);
pthread_join(tid[2], (void **)&key_pressed);

if (*key_pressed == 0x001b) {
break;
} else {
free(key_pressed);
}
}
if (key_pressed != NULL) {
free(key_pressed);
}
printf("\033[2J\033[H\033[?1049l");
return 0;
}

scr.h 文件将终端模式更改为非规范模式(从此处调用上面的 kbget() 函数):我认为 scr.h 中没有任何问题,因为它取自此处 (Move the cursor in a C program)。

scr.h:

#ifndef SCR_H
#define SCR_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

struct termios term, oterm;

int getch(void)
{
int c = 0;
tcgetattr(STDIN_FILENO, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
return c;
}

int kbhit(void)
{
int c = 0;

tcgetattr(STDIN_FILENO, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
if (c != -1) { ungetc(c, stdin); }
return c != -1 ? 1 : 0;
}

int kbesc(void)
{
int c = 0;
if (!kbhit()) { return 0x001b; } // 0x001b is the <ESC> key
c = getch();
if (c == 0) { while (kbhit()) { getch(); } }
return c;
}

int kbget(void)
{
int c = getch();
return c == 0x001b ? kbesc() : c; // 0x001b is the <ESC> key
}
#endif // SCR_H

在使用 valgrind 执行时,我在上面的代码中使用 pthread 也得到错误 Invalid write of size 4:

gcc -Wall scr.h main.c -pthread -o main
valgrind -v --leak-check=yes ./main

我知道 ncurses 和 pdcurses 的存在。我这样做只是为了锻炼自己。

更新

我已将我的代码更改为以下内容,不幸的是 ret 变量永远不会更改为 -1:

#include "scr.h"
#include <errno.h>
#include <sys/ioctl.h>

#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))

typedef struct {
int y_size;
int x_size;
} Window;

static int sigwinch_arrived = 0;

void sigwinch_handler(int signum)
{ sigwinch_arrived = 1; }

void on_window_size_change(Window *w)
{
struct winsize w_s;
// evaluate terminal size
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w->y_size = w_s.ws_row;
w->x_size = w_s.ws_col;
}

// print terminal size in its center
gotoyx(w->y_size / 2, w->x_size / 2);
del_from_cursor(15);
printf("w.y_size: %d", w->y_size);
gotoyx((w->y_size / 2) + 1, w->x_size / 2);
del_from_cursor(15);
printf("w.x_size: %d", w->x_size);
}

int main(void)
{
printf("\033[?1049h\033[2J\033[H");

gotoyx(1, 10);
printf("Press <ESC> to stop program.");
gotoyx(2, 10);
printf("Resizing the terminal window does not 'automatically' update the size shown on screen");

Window w;

int ret;
while (1) {

// get key pressed by user in terminal & exit if <ESC> is pressed
ret = kbget();
gotoyx(10, 10);
del_from_cursor(8);
printf("ret: %d", ret);
if (ret == -1) {
if (errno == EAGAIN) {
if (sigwinch_arrived) {
sigwinch_arrived = 0;
on_window_size_change(&w);
}
}
} else if (ret == 0x001b) {
break;
}
}
printf("\033[2J\033[H\033[?1049l");
return 0;
}

最佳答案

扩展名:根据 this answer ,如果您的 ncurses 是使用 --enable-sigwinch 标志编译的,它会自动执行下面的解决方案(如果您在 ncurses_init()< 之前没有覆盖 SIGWINCH/还没有)。在这种情况下,如果发生调整大小事件,getch() (wgetch()) 将简单地返回 KEY_RESIZE


如果您的控制字符终端的大小发生变化,您的进程应该会收到一个 SIGWINCH 信号(窗口大小发生变化,在 Linux 上为 28 信号)。

可以由内核发送(如果字符桌面有模式开关),也可以由虚拟终端软件(xterm、gnome-terminal、screen等)发送。

如果您的进程收到信号,它的阻塞内核调用(包括 getch())将停止并返回 -EAGAIN 错误号。这意味着,由于信号到达,阻塞调用提前停止。

请注意,从信号处理程序中,您不能做太多(例如:没有 malloc()),并且尽可能少地做最好的事情。典型的信号处理程序更改静态全局变量,其值由主程序检查。

未经测试的示例代码:

static int sigwinch_arrived = 0;

// this is called from the signal handler - nothing complex is allowed here
void sigwinch_handler(int signum) {
sigwinch_arrived = 1;
}

// callback if there is a window size change
void on_window_size_change() {
...
}

// main program
...
while (1) { // your main event handler loop
int ret = getch();
if (ret == ERR) {
if (errno == EAGAIN) {
if (sigwinch_arrived) {
sigwinch_arrived = 0;
on_window_size_change();
}
}
}
...
}

关于c - C中同时独立执行两个函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57736718/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com