目录
前言
我们上节提出了两个死循环不能同时运行,导致我们无法控制贪吃蛇的运动方向,本节我们便来解决这个问题。
Linux线程概念
线程是一个进程内部的控制序列,更准确的定义是:线程是一个进程内部的控制序列。 一切进程都至少有一个执行线程。 线程在进程内部运行本质是在进程的地址空间内运行。 Linux中,在CPU眼中看到的PCB都比传统的进程更加轻量化。 透过进程的虚拟地址空间可以看到进程的大部分资源,将进程的资源合理分配给每个执行流,就形成了线程执行流。
对于我们上节代码中:
int main()
{
int key;
initNcurse();
initSnake();
gamePic();
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(100000);
}
while (1)
{
key = getch();
switch (key)
{
case 0402:
printw("DOWN\n");
break;
case 0403:
printw("UP\n");
break;
case 0404:
printw("LEFT\n");
break;
case 0405:
printw("RIGHT\n");
break;
}
}
getch();//防止程序退出
endwin();
return 0;
}
程序永远都不会执行下面那个循环里面的内容。(因为程序卡死在下面这个代码中)
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(100000);
}
因为这个是while(1)死循环。
解决方法
那么我们有什么方法可以解决这个问题,此时我们就要引入Linux线程这个概念了,
Linux线程简单理解如图:
我们给予了一个Linux的多线程程序参考使用:
#include <stdio.h>
#include <pthread.h>
void* thread( void *arg )
{
printf( "This is a thread and arg = %d.\n", *(int*)arg);
*(int*)arg = 0;
return arg;
}
int main( int argc, char *argv[] )
{
pthread_t th;
int ret;
int arg = 10;
int *thread_ret = NULL;
ret = pthread_create( &th, NULL, thread, &arg );
if( ret != 0 ){
printf( "Create thread error!\n");
return -1;
}
printf( "This is the main process.\n" );
pthread_join( th, (void**)&thread_ret );
printf( "thread_ret = %d.\n", *thread_ret );
return 0;
}
我们将上面的代码保存,取名为test1.c。
我们打开终端准备编译运行这个文件:
上述内容演示是在macOS Sonoma14.4.1版本中,
直接输入“gcc test1.c”也可以直接运行。
但是在其他Linux环境中,可能要加入以下指令:
gcc test1.c -o thread -lpthread
上面的图片可以看出来,我这个系统不管那个指令都编译出了正确的可执行文件。
现在我们只需要模仿创造线程就可以解决这个问题。
注意事项⚠️
我们要包含创造线程的头文件“#include <pthread.h>”
解决问题🔥
通过多线程解决问题之前,我们先来封装两个函数:
void refreshJieMian()
{
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(100000);
}
}
refreshJieMian()封装效果用来不停地刷新界面。
void changeDir()
{
int key;
while (1)
{
key = getch();
switch (key)
{
case 0402:
printw("DOWN\n");
break;
case 0403:
printw("UP\n");
break;
case 0404:
printw("LEFT\n");
break;
case 0405:
printw("RIGHT\n");
break;
}
}
}
changeDir()函数用来检测用户的按键输入。
pthread_t t1;
pthread_t t2;
pthread_create( &t1, NULL, refreshJieMian, NULL);
pthread_create( &t2, NULL, changeDir, NULL);
我们将key设置全局变量,用于检测多线程是否生效。
#include <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct Snake
{
int hang;
int lie;
struct Snake * next;
};
struct Snake * head = NULL;
struct Snake * tail = NULL;
int key;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i,int j)
{
struct Snake * p;
p = head;
while(p != NULL)
{
if(p->hang == i && p->lie == j)
{
return 1;
}
p = p -> next;
}
return 0;
}
void gamePic()
{
int hang;
int lie;
move(0,0);
for(hang = 0;hang < 20;hang ++)
{
if(hang == 0)
{
for(lie = 0;lie < 20;lie ++)
{
printw("--");
}
printw("\n");
}
if(hang >= 0 && hang <= 19)
{
for(lie = 0;lie <= 20;lie ++)
{
if(lie == 0 || lie == 20) printw("|");
else if(hasSnakeNode(hang,lie)) printw("[]");
else printw(" ");
}
printw("\n");
}
if(hang == 19)
{
for(lie = 0;lie < 20;lie ++)
{
printw("--");
}
printw("\n");
printw("by beiweiqiuAC,%d\n",key);
}
}
}
void addNode()
{
struct Snake * new = (struct Snake *)malloc(sizeof(struct Snake));
new->hang = head->hang;
new->lie = tail->lie+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake(){
struct Snake * p;
while(head != NULL)
{
p = head;
head = head -> next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deleNode()
{
// struct Snake * p;
// p = head;
head = head ->next;
// free(p);
}
void moveSnake()
{
addNode();
deleNode();
if(tail ->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail ->lie == 20)
{
initSnake();
}
}
void* refreshJieMian()
{
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(100000);
}
}
void* changeDir()
{
while (1)
{
key = getch();
switch (key)
{
case 0402:
printw("DOWN\n");
break;
case 0403:
printw("UP\n");
break;
case 0404:
printw("LEFT\n");
break;
case 0405:
printw("RIGHT\n");
break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamePic();
pthread_create( &t1, NULL,refreshJieMian, NULL);
pthread_create( &t2, NULL, changeDir, NULL);
while(1);
getch();//防止程序退出
endwin();
return 0;
}
运行效果如下图所示:
我们每按下上下左右键程序都会给出反馈,并且小蛇也在一起运动,代表多线程成功创建。
总结
我们本节主要使用了Linux多线程来解决了两个死循环的问题,这也是多线程的初步使用和认识。