Linux C 多线程

2023-10-29

为什么会有线程?  

      ————————>>>>        进程实现多任务的缺点

  1. 进程间切换的计算机资源开销很大,切换效率非常低
  2. 进程间数据共享的开销也很大

线程进程关系

  • 线程是进程的一个执行单元,是进程内的调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程
  • 同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
  • 进程退出,进程中所有线程全部退出;
  • 一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  • 线程不可能完全替代进程;
  • 线程拥有独立的属性;

线程是任务调度和执行的基本单位
 

线程的特点

线程不安全,不稳定,不健壮————(一个线程的释放可能会影响其他线程)

线程切换的开销很低————(实质就是函数的切换)

线程通信机制简单(但不安全)————(访问全局变量)

线程操作

线程函数(不是OS提供,不是系统调用API,而是线程库libpthread.a/.so,库函数则可以跨平台)

线程库和函数手册的安装

sudo apt-get install glibc-doc :安装线程库
sudo apt-get install manpages-posix-dev:安装线程库的函数手册

 

线程创建

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

执行顺序:先创建的先执行

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void *read_mouse(void *ptr)
{
    int mouse;
    while (1)
    {
        read(*((int *)ptr), &mouse, sizeof(int));
        printf("mouse = %d\n", mouse);
    }
}
void *read_keyboard(void *ptr)
{
    char buffer[1024] = "0";
    while (1)
    {
        read(0, buffer, sizeof(buffer));
        printf("%s\n", buffer);
        memset(buffer, 0, sizeof(buffer));
    }
}

int main(int argc, char **argv)
{
    pthread_t id1;
    pthread_t id2;
    int fd = open("/dev/input/mouse0", O_RDONLY);
    if (fd < 0)
    {
        perror("fd mouse is error");
        exit(-1);
    }
    if (pthread_create(&id1, NULL, read_mouse, (void *)(&fd)) < 0) // 传递给线程的参数
    {
        perror("pthread_creat 1 error");
        exit(-1);
    }
    if (pthread_create(&id2, NULL, read_keyboard, NULL) < 0)
    {
        perror("pthread_creat 2 error");
        exit(-1);
    }

    pause(); // 若无pause,则主进程执行完,所有的线程会结束;所以需要
    return 0;
}

封装:

void *read_mouse(void *ptr)
{
    int mouse;
    while (1)
    {
        int fd = open("/dev/input/mouse0", O_RDONLY);
        if (fd < 0)
        {
            perror("fd mouse is error");
            exit(-1);
        }
        read(fd, &mouse, sizeof(int));
        printf("mouse = %d\n", mouse);
    }
}
void *read_keyboard(void *ptr)
{
    char buffer[1024] = "0";
    while (1)
    {
        read(0, buffer, sizeof(buffer));
        printf("%s\n", buffer);
        memset(buffer, 0, sizeof(buffer));
    }
}

int main(int argc, char **argv)
{
    pthread_t id[2];
    void *(*ptr[2])(void *);
    ptr[0] = read_keyboard;
    ptr[1] = read_mouse;
    for (int i = 0; i < 2; ++i)
    {
        pthread_create(id + i, NULL, *(ptr+i), NULL);
    }
    pause(); // 若无pause,则主进程执行完,所有的线程会结束;所以需要
    return 0;
}

线程可以访问全局数据区

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

char buffer[1024];

void *write_data(void *)
{
    while (1)
    {
        scanf("%s", buffer);
    }
}
void *read_data(void *)
{
    while (1)
    {
        if (strlen(buffer) != 0)
        {

            printf("%s\n", buffer);
            memset(buffer,0,sizeof(buffer));
        }
        sleep(3);
    }
}
int main(int argc, char **argv)
{
    pthread_t id[2];
    void *(*func[2])(void *);
    func[0] = write_data;
    func[1] = read_data;
    for (int i = 0; i < 2; ++i)
    {
        pthread_create(id + i, NULL, *(func + i), NULL);
    }
    pause();
    return 0;
}

线程退出

1.被动退出:

主线程关闭子线程:
int pthread_cancel(pthread_t thread);
1)功能
当次线程是死循环时,可以调动这个函数主动取消该线程
2)返回值
成功返回0,失败返回非零错误号。
2〉参数
thread:要取消线程的ID号

int main(int argc, char **argv)
{
    pthread_t id[2];
    void *(*func[2])(void *);
    func[0] = write_data;
    func[1] = read_data;
    for (int i = 0; i < 2; ++i)
    {
        pthread_create(id + i, NULL, *(func + i), NULL);
    }

    sleep(5);
    pthread_cancel(id[1]);//五秒钟后,次线程被主线程取消了,则不再能读取到buffer内数据
    pause();
    return 0;
}

2.主动退出

2.1 void pthread_exit(void *retval);函数用于退出当前线程,并可选择返回一个指定的值(用的多)

2.2 return ;     也是直接退出(用得少)

void *read_data(void *)
{
    while (1)
    {
        if (strlen(buffer) != 0)
        {
            if (strcmp(buffer,"exit")==0)//如果读到了exit就退出
            {
                pthread_exit(NULL); // 读取到exit,就会主动的退出线程
            }
            printf("%s\n", buffer);
            memset(buffer, 0, sizeof(buffer));
        }
    }
}



void *read_data(void *)
{
    while (1)
    {
        if (strlen(buffer) != 0)
        {
            if (strcmp(buffer,"exit")==0)//如果读到了exit就退出
            {
               return NULL; // 读取到exit,就会主动的退出线程
            }
            printf("%s\n", buffer);
            memset(buffer, 0, sizeof(buffer));
        }
    }
}

3.注册线程退出处理函数

pthread_cleanup_push

pthread_cleanup_pop

必须成对出现,且不能在{ }的语句中间,(因为他们是宏定义,各包含一个括号

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

char buffer[1024];
void *read_exit_handler(void *arg)
{
    printf("%s\n", (char *)arg);
}
void *write_data(void *)
{
    while (1)
    {
        scanf("%s", buffer);
    }
}

void *read_data(void *)
{
    pthread_cleanup_push(read_exit_handler, (void *)"cleannup done ");
    while (1)
    {
        if (strlen(buffer) != 0)
        {
            if (strcmp(buffer, "exit") == 0) // 如果读到了exit就退出
            {
                pthread_exit(NULL); // 可以调用退出清理函数(出栈)
                // return NULL;        // 不可以调用退出清理函数(出栈)
            }
            printf("%s\n", buffer);
            memset(buffer, 0, sizeof(buffer));
        }
        sleep(1); // 无限的循环,会导致无法调用pthread_cancel(id[1]);来调用退出处理函数!!!!!所以给每次循环睡眠一秒
    }
    pthread_cleanup_pop(0); // 非零值表示会执行清理函数,零值表示不执行清理
//------------------------即使pthread_cleanup_pop(0),但如果还是遇到了      pthread_exit(NULL)和pthread_cancel    还是会执行清理函数-------------------------------------
}
int main(int argc, char **argv)
{
    pthread_t id[2];
    void *(*func[2])(void *);
    func[0] = write_data;
    func[1] = read_data;
    for (int i = 0; i < 2; ++i)
    {
        pthread_create(id + i, NULL, *(func + i), NULL);
    }
    sleep(3);
    pthread_cancel(id[1]); // 五秒钟后,次线程被主线程取消了,则不再能读取到buffer内数据
    pause();
    return 0;
}

多个线程的退出清理函数执行顺序:

char buffer[1024];
void *read_exit_handler(void *arg)
{
    printf("%s\n", (char *)arg);
}
void *read_exit_handler2(void *arg)
{
    printf("%s 2\n", (char *)arg);
}
void *write_data(void *)
{
    while (1)
    {
        scanf("%s", buffer);
    }
}

void *read_data(void *)
{
    pthread_cleanup_push(read_exit_handler, (void *)"cleannup done ");
    pthread_cleanup_push(read_exit_handler2, (void *)"cleannup done ");


    printf("hello world\n");
    pthread_cleanup_pop(!0);
    pthread_cleanup_pop(!0); // 非零值表示会执行清理函数,零值表示不执行清理
   
}
int main(int argc, char **argv)
{
    pthread_t id[2];
    void *(*func[2])(void *);
    func[0] = write_data;
    func[1] = read_data;
    for (int i = 0; i < 2; ++i)
    {
        pthread_create(id + i, NULL, *(func + i), NULL);
    }
    pause();
    return 0;
}

线程等待

1.等待的目的:

1.1保证线程的退出顺序:保证一个线程退出并且回收资源后允许下一个线程退出
1.2回收线程退出时的资源情况:保证当前线程退出后,创建的新线程不会复用刚才退出线程的地址空间
1.3
获得新线程退出时的结果是否正确的退出返回值,这个有点类似回收僵尸进程的wait,保证不会发生内存泄露等问题
 

2.pthread_join(id[1], &ret); // 会阻塞,直到线程结束,才会执行下面的代码;)

线程状态

        1.线程资源为什么不采用进程退出后一起回收?

        2.Linux下把线程分为可结合态和分离态

可结合态:(默认的状态) 这种状态下的线程是能被其它进程回收其资源或杀死,资源只能通过pthread_join来回收(不能自己释放,进程主动回收)

分离态:这种状态下的线程是不能够被其它进程回收其资源或杀死;存储资源在它终止时由系统自动释放

        3.如何避免多线程退出导致的内存泄漏

3.1 可结合线程都显示调用pthread_join回收

3.2 将其变成分离态的线程:

【      

3.2.1:int pthread_detach(pthread_t thread);        (不阻塞)
功能:        如果次线程的资源不希望别人调用pthread_join函数来回收的话,而是希望自己在结束时自动回收资源的话,就可以调用这个函教
这个函数的功能就是分离次线程,让次线程在结束时自动回收资源
返回值:        成功返回0,失败返回错误号
参数:        thread:你要分离的那个次线程的TID

int main(int argc, char **argv)
{
    pthread_t id[2];
    void *(*func[2])(void *);
    func[0] = write_data;
    func[1] = read_data;
    for (int i = 0; i < 2; ++i)
    {
        if (pthread_create(id + i, NULL, *(func + i), NULL) < 0)
        {
            perror("pthread creat error");
            exit(-1);
        }
        pthread_detach(id[i]); // 设置为分离态,  资助回收资源,减少内存泄漏
        //此时也不需要pthread_join来回收资源了    
}
    pause();
    return 0;
}

3.2.2 修改属性

线程实现,向毅个文件里分别写如“hhhhhwwwww”和“helloworld”

线程同步(难点)

       

         1.进程vs线程

进程:进程空间天然是独立的,因此进程间资源的保护是天然的(现成的),需要重点关心的进程间的通信
线程:多线程天然的共享进程空间,因此线程数据共享是天然的(现成的),需要重点关心的是资源的保护

        2.线程的资源保护机制

2.1互斥锁

定义一个互斥锁(变量)

初始化互斥锁:预设互斥锁的初始值

1.直接赋值

2.初始化互斥锁的函数:int pthread_mutex_init ( pthread_mutex_t *restrict mutex,const pthread mutexattr_t *restrict attr);
功能:初始化定义的互斥锁
什么是初始化:就是设置互斥锁所需要的值。
返回值:总是返回0,所以这个函数不需要进行出错处理。
参数:~~~

【在C语言中,初始化静态全局变量时,只能使用常量表达式。PTHREAD_MUTEX_INITIALIZER`是一个宏,它并不是常量表达式,因此不能直接用于初始化静态全局变量。

如果你想在主函数内初始化互斥锁变量`mutex1`,可以使用`pthread_mutex_init`函数来实现。你可以将以下代码添加到主函数的开头,用于初始化互斥锁:
pthread_mutex_init(&mutex1, NULL);

这样,你就可以在主函数中正确地初始化互斥锁,并在其他线程中使用它。

对于你的代码,你可以这样修改:
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

或者使用`pthread_mutex_init`函数进行初始化:
pthread_mutex_t mutex1;
pthread_mutex_init(&mutex1, NULL);

这样修改后,`mutex1`将在主函数内正确地进行初始化。】

加锁、解锁

pthread_mutex_lock(&mutex1);————pthread_mutex_trylock( &mutex)(非阻塞加锁)

pthread_mutex_unlock(&mutex1);

进程退出时销毁互斥锁

void handler(int sig)
{
    pthread_mutex_destroy(&mutex1);
}
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

pthread_mutex_t mutex1= PTHREAD_MUTEX_INITIALIZER;//定义一个互斥锁变量 
//*****不能在主函数内调用这句话来初始化静态全局变量
int fd;
void handler(int sig)
{
    pthread_mutex_destroy(&mutex1);
}
void *write1(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&mutex1);
        write(fd, "hhhhh", 5);
        write(fd, "wwwww", 5);
        write(fd, "\n", 1);
        pthread_mutex_unlock(&mutex1);
    }
}
void *write2(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&mutex1);
        write(fd, "hello", 5);
        write(fd, "world", 5);
        write(fd, "\n", 1);
        pthread_mutex_unlock(&mutex1);
    }
}
int main(int argc, char **argv)
{
    // pthread_mutex_init(&mutex1, NULL);
    pthread_t id[2];
    void *(*pfunc[2])(void *);
    pfunc[0] = write1;
    pfunc[1] = write2;
    fd = open("a.txt", O_RDWR | O_CREAT | O_TRUNC, 0655);
    if (fd < 0)
    {
        perror("fd error");
        exit(-1);
    }
    for (int i = 0; i < 2; ++i)
    {
        if (pthread_create(&id[i], NULL, pfunc[i], NULL) < 0)
        {
            perror("pthread error");
            exit(-1);
        }
        pthread_detach(id[i]);
    }
    pause();
    signal(SIGINT, handler);
    return 0;
}

/--------------------------                 -----------------------------/
void *write1(void *arg)
{
    while (1)
    {
        if (pthread_mutex_trylock(&mutex1) == 0)//非阻塞的上锁,如果已经上锁,则返回0;
        {
            write(fd, "hhhhh", 5);
            write(fd, "wwwww", 5);
            write(fd, "\n", 1);
            pthread_mutex_unlock(&mutex1);
        }
        else
        {
            printf("trylock is busy\n");
        }
    }
}

注意事项:

1.凡是访问进程的全局变量,都要加锁;

2.涉及到共享资源的访问,必须上锁

【对于一个功能实现中,并不涉及访问共享资源的代码段,尽量不要放在上锁和解锁中间】

2.2线程信号量

2.2.1.定义信号量集合


2.2.2.初始化集合中的每个信号量


2.2.3.p、v操作

2.2.4.进程结束时,删除线程信号量集


 

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <semaphore.h>

sem_t sem1; // 1.定义信号量 (数组)
int fd;
pthread_t id[2];
void handler(int sig) // 4.注册的信号处理函数:用以删除信号量
{
    sem_destroy(&sem1);
}
void *write1(void *arg)
{
    while (1)
    {
        sem_wait(&sem1); // 3.p操作
        write(fd, "hhhhh", 5);
        write(fd, "wwwww", 5);
        write(fd, "\n", 1);
        sem_post(&sem1); // 3.v操作
    }
}
void *write2(void *arg)
{
    while (1)
    {
        sem_wait(&sem1);
        write(fd, "hello", 5);
        write(fd, "world", 5);
        write(fd, "\n", 1);
        sem_post(&sem1);
    }
}
int main(int argc, char **argv)
{
    sem_init(&sem1, 0, 1); // 2.初始化信号量
    void *(*pfunc[2])(void *);
    pfunc[0] = write1;
    pfunc[1] = write2;
    fd = open("c.txt", O_RDWR | O_CREAT | O_TRUNC, 0655);
    if (fd < 0)
    {
        perror("fd error");
        exit(-1);
    }
    for (int i = 0; i < 2; ++i)
    {
        if (pthread_create(&id[i], NULL, pfunc[i], NULL) < 0)
        {
            perror("pthread error");
            exit(-1);
        }
        pthread_detach(id[i]);
    }
    pause();
    signal(SIGINT, handler);
    return 0;
}

实现google面试题

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <signal.h>
#include <pthread.h>
#include <sys/stat.h>
#include <errno.h>

int fd[4];         // 四个文件描述符
sem_t sem_abcd[4]; // 含有四个信号量的数组
pthread_t id[4];   // 四个线程id的数组
void *write1(void *arg)
{
    int flag = 0;
    while (1)
    {
        sem_wait(&sem_abcd[0]);
        for (int i = 0; i < 4; ++i)
        {
            if (flag != 0 || i <= 0)
            {
                write(fd[i], "A", 1);
            }
        }
        flag = 1;
        sleep(1);
        sem_post(&sem_abcd[1]);
    }
}
void *write2(void *arg)
{
    int flag = 0;
    while (1)
    {
        sem_wait(&sem_abcd[1]);
        for (int i = 0; i < 4; ++i)
        {
            if (flag != 0 || i <= 1)
            {
                write(fd[i], "B", 1);
            }
        }
        flag = 1;
        sleep(1);
        sem_post(&sem_abcd[2]);
    }
}
void *write3(void *arg)
{
    int flag = 0;
    while (1)
    {
        sem_wait(&sem_abcd[2]);
        for (int i = 0; i < 4; ++i)
        {
            if (flag != 0 || i <= 2)
            {
                write(fd[i], "C", 1);
            }
        }
        flag = 1;
        sleep(1);
        sem_post(&sem_abcd[3]);
    }
}
void *write4(void *arg)
{
    int flag = 0;
    while (1)
    {
        sem_wait(&sem_abcd[3]);
        for (int i = 0; i < 4; ++i)
        {
            if (flag != 0 || i <= 3)
            {
                write(fd[i], "D", 1);
            }
        }
        flag = 1;
        sleep(1);
        sem_post(&sem_abcd[0]);
    }
}

int main(int argc, char **argv)
{
    char temp[1] = "A";
    for (int i = 0; i < 4; ++i)
    {
        temp[0] = temp[0] + i;
        if ((fd[i] = open(temp, O_CREAT | O_RDWR | O_TRUNC, 0655)) < 0) // 打开四个文件
        {
            perror("open error");
            exit(-1);
        }
        temp[0] = 'A';
    }

    void *(*pfunc[4])(void *) = {
        // 函数指针数组
        write1,
        write2,
        write3,
        write4,
    };

    for (int i = 0; i < 4; ++i)
    {
        if (i == 0)
        {
            sem_init(&sem_abcd[i], 0, 1); // 先初始化第一个线程的信号量————置为1
        }
        else
        {
            sem_init(&sem_abcd[i], 0, 0); // 其他线程的信号量全关闭————置为0
        }

        if (pthread_create(&id[i], NULL, pfunc[i], NULL) < 0) // 线程创建
        {
            perror("pthread creat error");
            exit(-1);
        }
        pthread_detach(id[i]); // 分离态
    }
    pause();
    return 0;
}

功能封装:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <signal.h>
#include <pthread.h>
#include <sys/stat.h>
#include <errno.h>

int fd[4];         // 四个文件描述符
sem_t sem_abcd[4]; // 含有四个信号量的数组
pthread_t id[4];   // 四个线程id的数组
int flag = 0;

void *write_demo(void *arg)
{
    char c[1] = "A";
    int num = *(int *)arg;
    c[0] = 'A' + num;
    while (1)
    {
        sem_wait(&sem_abcd[num]);
        for (int i = 0; i < 4; ++i)
        {
            if (flag >= 4 || i <= num)
            {
                write(fd[i], c, 1);
            }
        }
        flag++;
        sleep(1);
        sem_post(&sem_abcd[(num + 1) % 4]);
    }
}

int main(int argc, char **argv)
{
    char temp[1] = "A";
    int demo[4] = {0, 1, 2, 3};
    for (int i = 0; i < 4; ++i)
    {
        temp[0] = temp[0] + i;
        if ((fd[i] = open(temp, O_CREAT | O_RDWR | O_TRUNC, 0655)) < 0) // 打开四个文件
        {
            perror("open error");
            exit(-1);
        }
        temp[0] = 'A';
    }

    for (int i = 0; i < 4; ++i)
    {
        if (i == 0)
        {
            sem_init(&sem_abcd[i], 0, 1); // 先初始化第一个线程的信号量————置为1
        }
        else
        {
            sem_init(&sem_abcd[i], 0, 0); // 其他线程的信号量全关闭————置为0
        }

        if (pthread_create(&id[i], NULL, write_demo, (void *)(demo + i)) < 0) // 线程创建
        {
            perror("pthread creat error");
            exit(-1);
        }
        pthread_detach(id[i]); // 分离态
    }
    pause();
    return 0;
}
2.3条件变量

作用:多线程配合工作时,当线程检测到某条件不满足时就休眠,直到别的线程将条件准备好然后通过条件变量将其唤醒

【需要和互斥锁配合】

1.定义条件变量:pthread_cond_t;

2.初始化:

1.pthread_cond_init(pthread_cond_t *restrict cond,const thread_condattr_t * restrict attr)

2.赋值初始化:pthread_cond_t cond=PTHREAD_COND_INITLIALIZER

3.等待条件的函数

pthread_cond_wait(&cond, &mutex);                 // 不满足条件则阻塞等待,并释放互斥锁

4.满足条件,通知阻塞的线程

pthread_cond_signal(&cond);                                 // 满足条件,则通知因cond这一条件变量而阻塞等待的线程,
或者: pthread_cond_broadcast(&cond);唤醒所有睡眠的进程

5.销毁条件变量和互斥锁

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>

pthread_t id[2];
int val = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t cond; // 1.定义条件变量
// 条件变量的初始化可以      1.在主函数内调用pthread_cond——init初始化;  2.也可以用PTHREAD_COND_INITIALIZER赋值;

void handler_exit(int sig)
{
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond); // 5.注册退出处理函数,删除条件变量
    printf(" pthread_cond is cancel\n");
}
void *fun1(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&mutex);
        val++;
        if (val == 2)
        {                               // 通知线程2打印
            pthread_cond_signal(&cond); // 4.满足条件,则通知因cond这一条件变量而阻塞等待的线程,
            // pthread_cond_broadcast(&cond);唤醒所有睡眠的进程
        }

        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}
void *fun2(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&mutex);
        if (val != 2)
        {
            pthread_cond_wait(&cond, &mutex); // 3.不满足条件则阻塞等待,并释放互斥锁
        }
        printf("val == 2\n");
        val = 0;
        pthread_mutex_unlock(&mutex);
    }
}
int main(int argc, char **argv)
{
    signal(SIGINT, handler_exit);
    pthread_cond_init(&cond, NULL); // 2.初始化条件变量
    void *(*pfun[2])(void *arg) = {fun1, fun2};
    for (int i = 0; i < 2; ++i)
    {
        if (pthread_create(&id[i], NULL, pfun[i], NULL) < 0)
        {
            perror("pthread_creat error");
            exit(-1);
        }
        pthread_detach(id[i]);
    }
    pause();
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux C 多线程 的相关文章

  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • ALSA:snd_pcm_writei 调用时缓冲区不足

    当运行我最近从灰烬中带回来的旧程序时 我遇到了缓冲区不足的情况 该程序将原始声音文件完全加载到内存中 2100 字节长 525 帧 并准备 ALSA 进行输出 44 1khz 2 通道 有符号 16 位 if err snd pcm set
  • 为什么 Linux 没有 DirectX API?

    在考虑现代显卡的 Windows 系统上 DirectX API 的驱动程序端实现时 我想知道为什么此实现在非 Windows 系统 尤其是 Linux 上不可用 由于明显缺乏此功能 我只能假设有一个我无视的充分理由 但在我的原始理解中 我
  • 我们真的应该使用 Chef 来管理 sudoers 文件吗?

    这是我的问题 我担心如果 Chef 破坏了 sudoers 文件中的某些内容 可能是 Chef 用户错误地使用了说明书 那么服务器将完全无法访问 我讨厌我们完全失去客户的生产服务器 因为我们弄乱了 sudoers 文件并且无法再通过 ssh
  • 在centos中安装sqlite3 dev和其他包

    我正在尝试使用 cpanel 在 centos 机器上安装 sqlite dev 和其他库 以便能够编译应用程序 我对 debian 比 centos 更熟悉 我知道我需要的库是 libsqlite3 dev libkrb5 dev lib
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • 执行“minikube start”命令时出现问题

    malik malik minikube start minikube v1 12 0 on Ubuntu 18 04 Using the docker driver based on existing profile Starting c
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • 尽管 if 语句,Visual Studio 仍尝试包含 Linux 标头

    我正在尝试创建一个强大的头文件 无需更改即可在 Windows 和 Linux 上进行编译 为此 我的包含内容中有一个 if 语句 如下所示 if defined WINDOWS include
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • 如何查找哪个 Yocto 项目配方填充图像根文件系统上的特定文件

    我经常与 Yocto 项目合作 一个常见的挑战是确定文件为何 或来自什么配方 包含在 rootfs 中 这有望从构建系统的环境 日志和元数据中得出 理想情况下 一组命令将允许将文件链接回源 即配方 我通常的策略是对元数据执行搜索 例如gre
  • 如何使用Android获取Linux内核的版本?

    如何在 Android 应用程序中获取 Linux 内核的版本 不是 100 确定 但我认为调用 uname r 需要 root 访问权限 无论如何 有一种不太肮脏的方法可以做到这一点 那就是 System getProperty os v
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • Linux/POSIX:为什么 fork() 不分叉*所有*线程

    众所周知 POSIX下创建新进程的默认方式是使用fork 在 Linux 下 这在内部映射到clone 我想知道的是 众所周知 当一个人打电话时fork 子进程是用单个线程创建的 调用的线程fork cf https linux die n
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • 使用os.execlp时,为什么`python`需要`python`作为argv[0]

    代码是这样的 os execlp python python child py other args this works os execlp python child py other args this doesn t work 我读过
  • 无法显示 Laravel 欢迎页面

    我的服务器位于 DigitalOcean 云上 我正在使用 Ubuntu 和 Apache Web 服务器 我的家用计算机运行的是 Windows 7 我使用 putty 作为终端 遵循所有指示https laracasts com ser

随机推荐

  • eix安装_U盘安装原版Windows7

    教程简介 本教程为U盘安装原版Windows 7 我将带领大家学习如何用U盘安装原版的Windows7系统 希望对大家有帮助 在开始安装之前需要了解常见电脑的U盘启动按键都有哪些 请仔细阅读下表 安装步骤 一定要看步骤3 不然造成数据损坏概
  • 爬虫入门第2课:代理池的设计

    爬虫学习知识点及案例篇 汇总 爬虫入门第1课 代理池概述及开发环境 本阶段带大家从代理池的设计开始 学习Python爬虫及项目实战 详情关注上方专栏 1 代理池的工作流程 目标 理解代理池的工作流程 以及 各个模块的作用 内容介绍 代理池的
  • Java-API简析_java.net.InetAddress类(基于 Latest JDK)(浅析源码)

    版权声明 未经博主同意 谢绝转载 请尊重原创 博主保留追究权 https blog csdn net m0 69908381 article details 131590559 出自 进步 于辰的博客 因为我发现目前 我对Java API的
  • 模板引擎 template

    1 特性 性能卓越 执行速度通常是Mustache与tmpl的20多倍 性能测试 支持运行时调试 可精确定位异常模板所在的语句 对NodeJS Express友好支持 安全 默认对输出进行转义 在沙箱中运行编译后的代码 Node版本可以安全
  • SQL Server批处理运行时错误的影响

    前言 批处理是同时从应用程序发送到 SQL Server 2005 并得以执行的一组单条或多条 Transact SQL 语句 我们通常认为当一个批处理的多条语句中有一条发生运行时错误 将停止执行批处理中当前语句和它之后的语句 这使得在实际
  • Seq2Seq实战——机器翻译

    基于seq2seq做一个机器翻译 我们将使用PyTorch和TorchText构建一个机器学习模型 从一个序列到另一个序列 将德语到英语翻译成英语 该模型是 Sequence to Sequence Learning with Neural
  • 简单html文件上传带参数

    html的代码如下
  • 2021SC@SDUSC Zxing开源代码(十六)PDF417二维码(二)

    2021SC SDUSC 目录 一 BarcodeMatrix 二 BarcodeRow 三 Compaction 四 Dimensions 五 PDF417ErrorCorrection 六 PDF417HighLevelEncoder
  • [创业之路-57] :商业计划书BP如何书写?总体框架!

    引言 BP Buiness Plan 即商业计划书 本质上还是一份计划 是一份商业计划 即一种关于如何赚钱的计划 是一份通过组建公司 运营项目 进而赚钱的项目计划 什么是商业 商业 是一种有组织的提供顾客所需的物品与服务 都可以称为产品 的
  • 逻辑运算符&&、&、

    运算符 和 以及 和 的区别 逻辑运算符 与 只要有一边为fale 那么就是false 或 只要有一边为true 那么就是true 异或 只要是相同的boolean值 那么就是false 不相同才是true 逻辑运算符 双与 双或 解释 双
  • Qt小项目1 计算器

    头文件 ifndef WIDGET H define WIDGET H include
  • 资产设备管理系统方案,什么是智能设备管理系统?

    文章目录 一 设备信息管理 二 设备使用管理 三 设备租赁管理 四 设备运行管理 五 设备维修维保管理 六 资产设备管理应用价值 茗鹤设备管理信息系统以资产设备和备件为基本管理对象 覆盖资产生命周期 调试 运行 计划 维护 修复 分析和报废
  • 安陌陌咩咩咩

    include
  • virtualBox虚拟机没有64位选项

    问题解决 首先 你要确认你的CPU是64位的 如果是那么继续查看 然后 进入BIOS将 intel virtual technology 设置为 enable 如何进入BIOS 自行百度 根据自己电脑型号搜索一般是开机按F2 联想z470
  • react学习笔记-从井字棋开始(2)

    前言 接着前面的空格子棋 加上其他功能变成真正的井字棋 数字显示添加 传值 prop 方式 修改 Board 类的renderSquare函数 class Board extends React Component renderSquare
  • 一个数组实现两个栈(共享栈)

    题目 一个数组实现两个栈 方法1 下标为0的位置为栈1的栈底 下标为1的位置为栈2的栈底 栈1的元素存放在下标为偶数的位置上 栈2的元素放在下标为奇数的位置上 如上图所示的数组 若栈1有一个元素 2 栈2有6个元素 1 2 3 4 5 6
  • UnityShader _Object2World与UNITY_MATRIX_MVP被替换

    1 Object2World 模型空间转世界空间 Unity5 5版本中 Object2World已经变成unity ObjectToWorld World2Object也变成了unity WorldToObject 但由于Unity的向下
  • Macos安装brew攻略

    Brew Macos的apt get和软件安装快捷方式 Brew install offiical site https brew sh install instruction bin bash c curl fsSL https raw
  • java批量插入数据库之写绑定变量

    最近查到关于sql批量写入和绑定变量的问题 看到了很多好的帖子 再次进行总结 数据库在执行SQL语句时会首先解析SQL语句 解析又分为硬解析与软解析 说到硬解析和软解析 就不能不说一下Oracle对sql的处理过程 当你发出一条sql语句交
  • Linux C 多线程

    为什么会有线程 gt gt gt gt 进程实现多任务的缺点 进程间切换的计算机资源开销很大 切换效率非常低 进程间数据共享的开销也很大 线程和进程的关系 线程是进程的一个执行单元 是进程内的调度实体 比进程更小的独立运行的基本单位 线程也