Linux 多线程 pthread库用法(一)

Linux 多线程编程基介绍

Linux 线程有时候也叫 Light Weight Process LWP 轻量级线程,是进程的一个执行流,有自己的执行栈,是操作系统调度的最小单位。 多线程优势在于切换开销小,同进程内通信方便,涉及IO等阻塞性操作时可以单独开一个线程不阻塞主流程,不足之处健壮性不如多进程,一个线程crash,那么所在进程就都crash了。这两篇入门的文章写的不错:

本文主要总结下Linux多线程库 pthread 的最基本用法,进一步使用后面文字再介绍。

创建线程的函数接口 phtread_create

创建线程的 Linux 库函数,在 /usr/include 目录下, 声明如下:

/* Create a new thread, starting with execution of START-ROUTINE
   getting passed ARG.  Creation attributed come from ATTR.  The new
   handle is stored in *NEWTHREAD.  */
extern int pthread_create (pthread_t *__restrict __newthread,
               const pthread_attr_t *__restrict __attr,
               void *(*__start_routine) (void *),
               void *__restrict __arg) __THROWNL __nonnull ((1, 3));
// (gdb) pt pthread_t
// type = unsigned long ,也可以在库函数中看到其定义,ptype 层层展开声明,最终都能到基本数据类型
// mac os 中 pthread 是一个结构体

// 定义在 pthreadtypes.h 中
/* Thread identifiers.  The structure of the attribute type is not
   exposed on purpose.  */
typedef unsigned long int pthread_t;

union pthread_attr_t
{
  char __size[__SIZEOF_PTHREAD_ATTR_T];
  long int __align;
};
// pthread_attr_t 在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化

#ifdef __x86_64__
# if __WORDSIZE == 64
#  define __SIZEOF_PTHREAD_ATTR_T 56

函数声明中用到了 restrict 关键字,详细理解此关键字看这里C语言关键字restrict。由 restrict 修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。由 restrict 修饰的指针主要用于函数形参,或指向由 malloc() 分配的内存空间。restrict 数据类型不改变程序的语义。 编译器能通过作出 restrict 修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。Linux man pages

  • 第一个参数为指向线程标识符的指针。
  • 第二个参数用来设置线程属性. 具体介绍可以看:Posix多线程编程—线程属性
  • 第三个参数是线程运行函数的起始地址。
  • 第四个参数是运行函数的参数。

一个 pthread 的最基础使用例子

跑起来一个Linux多线程程序,可以用 ps ufx|grep XXX (XXX 程序的名字) 查看进程的状态,l就是多线程状态的意思,还可以看进程启动时间,运行的cpu时间。 man ps有详细解释:

  • D uninterruptible sleep (usually IO)
  • R running or runnable (on run queue)
  • S interruptible sleep (waiting for an event to complete)
  • T stopped, either by a job control signal or because it is being traced
  • W paging (not valid since the 2.6.xx kernel)
  • X dead (should never be seen)
  • Z defunct (“zombie”) process, terminated but not reaped by its parent

BSD formats, Linux 下执行都会有:

  • < high-priority (not nice to other users)
  • N low-priority (nice to other users)
  • L has pages locked into memory (for real-time and custom IO)
  • s is a session leader
  • l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
  • + is in the foreground process group
    下面是一个最基本的例子:
/* 举例三个线程: 主线程 + create 两个 */
/* 可以直接在 xcode 中执行,如果在linux 上跑编译的时候用下面的makefile, 主要是需要 -lpthread */
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread1_main(void *p)
{
    while(1) {
        printf("This is thread 1\n");
        sleep(1);
    }
    return NULL;
}

void* thread2_main(void *p)
{
    while(1) {
        printf("This is thread 2\n");
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t tid1, tid2;
    void *ret1, *ret2;
    pthread_create(&tid1, NULL, thread1_main, NULL);
    pthread_create(&tid2, NULL, thread2_main, NULL);
    while (1) {
        printf("This is thread main\n");
        sleep(1);
    }
    pthread_join(tid1, &ret1);
    pthread_join(tid2, &ret2);
    return 0;
}

一个简单的makefile

# 编译当前目录所有的 .c 文件,注意链接 -lpthread
# 开启所有 warning,按照错误处理
TARGETS = mytest.bin
CFLAGS  = -Wall -Werror -m64 -g3 -std=c99

$(TARGETS) : $(wildcard *.c)
    $(CC) $(CFLAGS) $^ -o $@ -lpthread

.PHONY : clean
clean:
    rm *.bin
Logo

更多推荐