(C语言贪吃蛇)12.Linux线程概念引入及编程实现

目录

前言

Linux线程概念

解决方法

注意事项⚠️

解决问题🔥

总结


前言

我们上节提出了两个死循环不能同时运行,导致我们无法控制贪吃蛇的运动方向,本节我们便来解决这个问题。

Linux线程概念

线程是一个进程内部的控制序列,更准确的定义是:线程是一个进程内部的控制序列 。 一切进程都至少有一个执行线程。 线程在进程内部运行本质是在进程的地址空间内运行。 Linux中,在CPU眼中看到的PCB都比传统的进程更加轻量化。 透过进程的虚拟地址空间可以看到进程的大部分资源,将进程的资源合理分配给每个执行流,就形成了线程执行流。

对于我们上节代码中:

cpp 复制代码
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的多线程程序参考使用:

cpp 复制代码
#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>"

解决问题🔥

通过多线程解决问题之前,我们先来封装两个函数:

cpp 复制代码
void refreshJieMian()
{
    while(1)
        {
            moveSnake();
            gamePic();
            refresh();
            usleep(100000);
        }
}

refreshJieMian()封装效果用来不停地刷新界面。

cpp 复制代码
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()函数用来检测用户的按键输入。

cpp 复制代码
    pthread_t t1;
    pthread_t t2;
    
    pthread_create( &t1, NULL, refreshJieMian, NULL);
    pthread_create( &t2, NULL, changeDir, NULL);

我们将key设置全局变量,用于检测多线程是否生效。

cpp 复制代码
#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多线程来解决了两个死循环的问题,这也是多线程的初步使用和认识。

相关推荐
汽车电子助手3 分钟前
【STM32开发环境搭建】-4-在STM32CubeMX中新增Keil(MDK-ARM) 5的工程目录(包含指定路径的C和H文件)
c语言·arm开发·stm32·stm32cubemx·keil
努力编程的阿伟8 分钟前
Java多态性:面向对象编程的精髓
java·开发语言
地球空间-技术小鱼16 分钟前
SQL常用语法
java·开发语言·前端
程序那点事儿28 分钟前
open-resty 服务安装kafka插件
linux·分布式·nginx·kafka·lua·运维开发·openresty
小程爱敲代码36 分钟前
Linux集群部署RabbitMQ
linux·运维·rabbitmq
橘子海全栈攻城狮39 分钟前
【源码+文档+调试讲解】交通信息管理系统
运维·开发语言·数据库·python·sqlite
zhangxueyi1 小时前
详解DNS工作原理及实例分析
linux·运维·服务器·dns
小O_好好学1 小时前
CentOS 6文件系统
linux·运维·centos
一张假钞1 小时前
Linux下Nodejs应用service配置
linux·运维·服务器·node.js
ccc_9wy1 小时前
vulnhub-matrix-breakout-2-morpheus靶机的测试报告
linux·web安全·网络安全·提权·webshell·渗透测试流程·黑客帝国2靶机