最近在看《unix/linux编程实践教程》,其中第十四章有一段代码,内容是任意输入一些字符串,每一个字符串由一个线程控制让它在终端以随机速度左右移动,主线程接收键盘字符0-9,来控制相应字符串移动方向。空格用来改变所有字符串运动方向。测试是在centos7的tty上进行的,当快速敲击键盘按键时,总是在终端某些位置出现莫名其妙的字符串,控制光标的函数在调用时已经上锁,初始化终端时调用了cbreak()和noecho(),想请教大牛为什么会出现终端显示莫名其妙字符的现象。代码如下(该代码做了一些修改,与书上源码不完全相同)。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<curses.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<time.h>
#include<errno.h>
#include<fcntl.h>
#define MAXMSG 12 /* limit to number of strings */
#define COLS 100
#define LINES 37
#define TUNIT 5000 /* timeunits in microseconds */
#define MSGLEN (COLS-2)
struct propset {
char *str; /* the message */
int row; /* the row */
int delay; /* delay in time units */
volatile int dir; /* +1 or -1 */
pthread_mutex_t myMutex;
};
typedef struct str_attribute
{
char ** avPtr;
int num;
}StrAttribute;
static pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;
static int num_msg;
static struct propset props[MAXMSG]; /* properties of string */
int setup(int nstrings, char *strings[], struct propset props[])
{
int fd=open("/dev/tty",O_RDONLY);
if(-1==dup2(fd,STDIN_FILENO))
{
perror(NULL);
exit(EXIT_FAILURE);
}
close(fd);
num_msg=nstrings;
int i;
srand((unsigned int)time(NULL));
for(i=0 ; i<num_msg; i++){
props[i].str = strings[i];
props[i].row = i;
props[i].delay = 1+(rand()%15);
props[i].dir = ((rand()%2)?1:-1);
pthread_mutex_init(&props[i].myMutex,NULL);
}
initscr();
cbreak();
noecho();
clear();
mvprintw(LINES-1,0,"'Q' to quit, '0'..'%d' to bounce",num_msg-1);
return num_msg;
}
void *animate(void *arg)
{
struct propset *info = (struct propset *)arg;
int len = strlen(info->str)+2;
int col = rand()%(COLS-len);
while(1)
{
usleep(info->delay*TUNIT);
pthread_mutex_lock(&mx);
move( info->row, col );
addch(' ');
addstr( info->str );
addch(' ');
move(LINES-1,COLS-1);
refresh();
pthread_mutex_unlock(&mx);
pthread_mutex_lock(&info->myMutex);
col += info->dir;
if ( col <= 0 && info->dir == -1 )
{
info->dir = 1;
usleep(info->delay*TUNIT);
pthread_mutex_lock(&mx);
move( info->row, col );
addch(' ');
addstr( info->str );
addch(' ');
move(LINES-1,COLS-1);
refresh();
pthread_mutex_unlock(&mx);
col+=info->dir;
}
else if ( col+len+1 >= COLS && info->dir == 1 )
{
info->dir = -1;
usleep(info->delay*TUNIT);
pthread_mutex_lock(&mx);
move( info->row, col );
addch(' ');
addstr( info->str );
addch(' ');
move(LINES-1,COLS-1);
refresh();
pthread_mutex_unlock(&mx);
col+=info->dir;
}
pthread_mutex_unlock(&info->myMutex);
}
}
static StrAttribute
getStr(void)
{
char buffer[MSGLEN]={0};
static char *avBuf[MAXMSG]={0};
unsigned char count=0;
StrAttribute ret;
while(count<MAXMSG && fgets(buffer,MSGLEN,stdin)!=NULL)
{
avBuf[count]=(char *)calloc(strlen(buffer)+1,sizeof(char));
strcpy(avBuf[count],buffer);
if(avBuf[count][strlen(buffer)-1]=='\n')
avBuf[count][strlen(buffer)-1]='\0';
memset(buffer,0,strlen(buffer));
++count;
}
ret.avPtr=avBuf;
ret.num=count;
return ret;
}
static void
freeStr(StrAttribute arg)
{
int i=0;
for(;i<arg.num;++i)
{
free(arg.avPtr[i]);
}
}
int main(void)
{
pthread_t thrds[MAXMSG]; /* the threads */
void *animate(); /* the function */
int num_msg ; /* number of strings */
int i;
StrAttribute ret;
int ac;
char **av;
int c;
ret=getStr();
ac=ret.num;
av=ret.avPtr;
num_msg = setup(ac,av,props);
/* create all the threads */
for(i=0 ; i<num_msg; i++)
if ( pthread_create(&thrds[i], NULL, animate, &props[i])){
fprintf(stderr,"error creating thread");
endwin();
exit(0);
}
/* process user input */
while(1)
{
c = getch();
if ( c == 'Q' )
break;
else if ( c == ' ' )
for(i=0;i<num_msg;i++)
{
pthread_mutex_lock(&props[i].myMutex);
props[i].dir = -props[i].dir;
pthread_mutex_unlock(&props[i].myMutex);
}
else if ( c >= '0' && c <= '9' )
{
i = c - '0';
if ( i < num_msg )
{
pthread_mutex_lock(&props[i].myMutex);
props[i].dir = -props[i].dir;
pthread_mutex_unlock(&props[i].myMutex);
}
}
}
/* cancel all the threads */
pthread_mutex_lock(&mx);
for (i=0; i<num_msg; i++ )
pthread_cancel(thrds[i]);
for(i=0;i<num_msg;++i)
pthread_join(thrds[i],NULL);
for(i=0;i<num_msg;++i)
pthread_mutex_destroy(&props[i].myMutex);
pthread_mutex_unlock(&mx);
freeStr(ret);
endwin();
return 0;
}