Linux多线程实践(2) --线程基本API

简介: POSIX线程库  与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头,要使用这些函数库,要通过引入头文,而且链接这些线程函数库时要使用编译器命令的“-l...

POSIX线程库

  与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头,要使用这些函数库,要通过引入头文<pthread.h>,而且链接这些线程函数库时要使用编译器命令的“-lpthread”选项[Ubuntu系列系统需要添加的是”-pthread”选项而不是”-lpthread”,如Ubuntu 14.04版本,深度Ubuntu等]

 

1.pthread_create

int pthread_create(pthread_t *restrict thread,
		const pthread_attr_t *restrict attr,
		void *(*start_routine)(void*), void *restrict arg);

创建一个新的线程

参数

  thread:线程ID

  attr:设置线程的属性,一般设置为NULL表示使用默认属性

  start_routine:是个函数地址,线程启动后要执行的函数

  arg:传给线程启动函数的参数

返回值:成功返回0;失败返回错误码;

 

附-Posix错误检查

  UNIX传统的函数:成功返回0,失败返回-1,并且对设置全局变量errno以指定错误类型。然而pthreads函数出错时不会设置全局变量errno(而其他的大部分POSIX函数会设置errno)。而是将错误代码通过返回值返回;

  pthreads同样也提供了线程内的errno变量,对于每一个线程, 都有一个errno的值, 以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值进行判定,因为读取返回值要比读取线程内的errno变量的开销更小!

/** 实践: 新的错误检查与错误退出函数 **/
inline void err_check(const std::string &msg, int retno)
{
    if (retno != 0)
        err_exit(msg, retno);
}
inline void err_exit(const std::string &msg, int retno)
{
    std::cerr << msg << ": " << strerror(retno) << endl;
    exit(EXIT_FAILURE);
}

2.pthread_exit

void pthread_exit(void *value_ptr);

线程终止

  value_ptr:指向该线程的返回值;注意:value_ptr不能指向一个局部变量

 

3.pthread_join

int pthread_join(pthread_t thread, void **value_ptr);

等待线程结束

  value_ptr:它指向一个指针,后者指向线程的返回值(用户获取线程的返回值)

/** 示例: 等待线程退出 **/
void *thread_rotine(void *args)
{
    for (int i = 0; i < 10; ++i)
    {
        printf("B");
        fflush(stdout);
        usleep(20);
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t thread;
    int ret = pthread_create(&thread, NULL, thread_rotine, NULL);
    err_check("pthread_create", ret);


    for (int i = 0; i < 10; ++i)
    {
        printf("A");
        fflush(stdout);
        usleep(20);
    }

    ret = pthread_join(thread, NULL);
    err_check("pthread_join", ret);
    putchar('\n');
    return 0;
}

4.pthread_self

pthread_t pthread_self(void);

返回线程ID

/** 示例:主控线程与子线程传递数据 **/
typedef struct _Student
{
    char name[20];
    unsigned int age;
} Student;

void *threadFunction(void *args)
{
    cout << "In Thread: " << pthread_self() << endl;
    Student tmp = *(Student *)(args);
    cout << "Name: " << tmp.name << endl;
    cout << "Age: " << tmp.age << endl;

    pthread_exit(NULL);
}

int main()
{
    Student student = {"xiaofang",22};

    pthread_t thread;
    //启动创建并启动线程
    pthread_create(&thread,NULL,threadFunction,&student);
    //等待线程结束
    pthread_join(thread,NULL);

    return 0;
}


5.pthread_cancel

int pthread_cancel(pthread_t thread);

取消一个执行中的线程


6.pthread_detach

int pthread_detach(pthread_t thread);

  将一个线程分离-如果在新创建的线程结束时主线程没有结束同时也没有调用pthread_join,则会产生僵线程,次问题可以通过设置线程为分离的(detach)来解决;

 

总结:进程 VS. 线程

进程(pid_t)

线程(pthread_t)

Fork

Pthread_create

Waitpit

Pthread_join/Pthread_detach

Kill

Pthread_cancel

Pid

Pthead_self

Exit/return

Pthread_exit/return

僵尸进程(没有调用wait/waitpid等函数)

僵尸线程(没有调用pthread_join/pthread_detach)

/** 将并发echo server改造成多线程形式 
注意线程竞速问题的解决
**/
void echo_server(int clientSocket);
void *thread_routine(void *arg);
int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if (sockfd == -1)
        err_exit("socket error");

    int optval = 1;
    if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1)
        err_exit("setsockopt error");

    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8002);
    serverAddr.sin_addr.s_addr = INADDR_ANY;    //绑定本机的任意一个IP地址
    if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
        err_exit("bind error");

    if (listen(sockfd,SOMAXCONN) == -1)
        err_exit("listen error");

    while (true)
    {
        int peerSockfd = accept(sockfd, NULL, NULL);
        if (peerSockfd == -1)
            err_exit("accept error");

        pthread_t tid;
        /**注意: 下面这种用法可能会产生"竞速问题"
                当另一个连接快读快速到达, peerSockfd的内容更改,
                新创建的线程尚未将该值取走时,线程读取的就不是
                我们想让线程读取的值了
        int ret = pthread_create(&tid, NULL, thread_routine, (void *)&peerSockfd);
        **/
        //解决方案: 为每一个链接创建一块内存
        int *p = new int(peerSockfd);
        int ret = pthread_create(&tid, NULL, thread_routine, p);
        if (ret != 0)
            err_thread("pthread_create error", ret);
    }
    close(sockfd);
}
void *thread_routine(void *args)
{
    //将线程设置分离状态, 避免出现僵尸线程
    pthread_detach(pthread_self());
    int peerSockfd = *(int *)args;
    //将值取到之后就将这块内存释放掉
    delete (int *)args;

    echo_server(peerSockfd);
    cout << "thread " << pthread_self() << " exiting ..." << endl;
    pthread_exit(NULL);
}
void echo_server(int clientSocket)
{
    char buf[BUFSIZ] = {0};
    int readBytes;
    while ((readBytes = read(clientSocket, buf, sizeof(buf))) >= 0)
    {
        if (readBytes == 0)
        {
            cerr << "client connect closed" << endl;
            break;
        }
        if (write(clientSocket, buf, readBytes) == -1)
        {
            cerr << "server thread write error" << endl;
            break;
        }
        cout << buf;
        bzero(buf, sizeof(buf));
    }
}

其完整源代码:download.csdn.net/detail/hanqing280441589/8440763


目录
相关文章
|
21天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
15 3
|
21天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
14 2
|
21天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
27 2
|
21天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
27 1
|
21天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
31 1
|
21天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
23 1
|
1月前
|
存储 运维 NoSQL
Redis为什么最开始被设计成单线程而不是多线程
总之,Redis采用单线程设计是基于对系统特性的深刻洞察和权衡的结果。这种设计不仅保持了Redis的高性能,还确保了其代码的简洁性、可维护性以及部署的便捷性,使之成为众多应用场景下的首选数据存储解决方案。
38 1
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
40 1
C++ 多线程之初识多线程
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
1月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
45 6