【面经笔记】线程、多线程

配置VS2013 使用pthread,参考:
http://blog.csdn.net/k459905889/article/details/48676159

pthread函数使用,参考:
http://blog.csdn.net/dreamintheworld/article/details/52577681


线程状态及结束资源回收:
http://blog.163.com/sunjinxia@126/blog/static/949848792013214386222/

线程创建的时候默认处于joinable状态,此状态线程结束的时候不会自动回收线程资源,需要pthread_join函数来回收;pthread_detach可以讲线程转换为detached状态,子线程运行完成之后可以自行回收资源。


多线程

线程创建:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

using namespace::std;

#define THREAD_NUMBER 3//线程数
#define REPEAT_NUMBER 5//每个线程中小任务数
#define DELAY_TIME_LEVELS 10.0//小任务之间的最大时间间隔

#pragma comment(lib,"pthreadVC2.lib") 

//线程函数
void * thrd_func(void * arg)
{
    int thrd_num = (int)arg;
    int delay_time = 0;
    int count = 0;
    printf("线程%d 开始\n", thrd_num);
    for (count = 0; count < REPEAT_NUMBER;count++)
    {
        delay_time = (int)(rand()*DELAY_TIME_LEVELS / (RAND_MAX)+1);
        Sleep(delay_time);
        printf("\t线程%d:job %d delay = %d\n", thrd_num, count, delay_time);
    }
    printf("线程%d 完成\n",thrd_num);
    pthread_exit(NULL);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t thread[THREAD_NUMBER];//线程的标识符
    int i = 0, res;
    srand(time(NULL));
    //创建线程
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_create(&thread[i], NULL, thrd_func, (void*)i);
        if (res)
        {
            printf("线程%d创建失败!\n", i);
            exit(res);
        }
    }
    printf("线程全部创建完成!\n等待线程结束、、、\n");
    //等待线程结束
    void* thrd_ret;
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_join(thread[i], &thrd_ret);//阻塞等待结束
        if (!res)
        {
            printf("线程%djoined\n", i);
        }
        else
        {
            printf("线程%djoined 失败!\n");
        }
    }
    return 0;
}
互斥锁使用:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

using namespace::std;

#define THREAD_NUMBER 3//线程数
#define REPEAT_NUMBER 5//每个线程中小任务数
#define DELAY_TIME_LEVELS 10.0//小任务之间的最大时间间隔

#pragma comment(lib,"pthreadVC2.lib") 

pthread_mutex_t mutex;//互斥锁

//线程函数
void * thrd_func(void * arg)
{
    int thrd_num = (int)arg;
    int delay_time = 0, count = 0;
    int res;
    res = pthread_mutex_lock( &mutex );
    if (res)
    {
        printf("线程%d 上锁失败\n", thrd_num);
        pthread_exit(NULL);
    }
    printf("线程%d 开始\n", thrd_num);
    for (count = 0; count < REPEAT_NUMBER;count++)
    {
        delay_time = (int)(rand()*DELAY_TIME_LEVELS / (RAND_MAX)+1);
        Sleep(delay_time);
        printf("\t线程%d:job %d delay = %d\n", thrd_num, count, delay_time);
    }
    printf("线程%d 完成\n",thrd_num);
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t thread[THREAD_NUMBER];//线程的标识符
    int i = 0, res;

    srand(time(NULL));

    //互斥锁初始化
    pthread_mutex_init(&mutex, NULL);


    //创建线程
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_create(&thread[i], NULL, thrd_func, (void*)i);
        if (res)
        {
            printf("线程%d创建失败!\n", i);
            exit(res);
        }
    }
    printf("线程全部创建完成!\n等待线程结束、、、\n");
    //等待线程结束
    void* thrd_ret;
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_join(thread[i], &thrd_ret);//阻塞等待结束
        if (!res)
        {
            printf("线程%djoined\n", i);

        }
        else
        {
            printf("线程%djoined 失败!\n");
        }
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

调用pthread_mutex_lock上锁,发现上锁时候后进入等待,等待锁再次释放后重新上锁;
所以线程程序加载到队列中等待,等待成功上锁后继续执行程序代码;

自旋锁使用:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <semaphore.h>
#include <iostream>

using namespace::std;

#define THREAD_NUMBER 3//线程数

#pragma comment(lib,"pthreadVC2.lib") 

pthread_spinlock_t spinlock;

void* thred_func(void* thrd_id)
{
    int id = (int)thrd_id; 
    pthread_spin_lock(&spinlock);

    cout <<"线程"<< id <<"执行!"<<endl;

    pthread_spin_unlock(&spinlock);
    pthread_exit(NULL);
    return NULL;
}
int main()
{
    pthread_t thread[THREAD_NUMBER];

    pthread_spin_init(&spinlock, 0);

    int res;
    for (auto i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_create(&thread[i], NULL, thred_func, (void*)i);
        if (res)
        {
            cout << "线程"<<i<<"创建失败!" << endl;
            exit(res);
        }
    }
    void * thrd_ret;
    for (auto i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_join(thread[i], &thrd_ret);
        if (!res)
        {
            cout << "线程" << i << "joined" << endl;
        }
        else
        {
            cout << "线程" << i << "joined 失败" << endl;
        }
    }
    pthread_spin_destroy(&spinlock);
    return 0 ;
}
信号量使用:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <semaphore.h>

using namespace::std;

#define THREAD_NUMBER 3//线程数
#define REPEAT_NUMBER 5//每个线程中小任务数
#define DELAY_TIME_LEVELS 10.0//小任务之间的最大时间间隔

#pragma comment(lib,"pthreadVC2.lib") 

sem_t sem[THREAD_NUMBER];//信号量

//线程函数
void * thrd_func(void * arg)
{
    int thrd_num = (int)arg;
    int delay_time = 0, count = 0;
    int res;

    sem_wait(&sem[thrd_num]);

    printf("线程%d 开始\n", thrd_num);
    for (count = 0; count < REPEAT_NUMBER;count++)
    {
        delay_time = (int)(rand()*DELAY_TIME_LEVELS / (RAND_MAX)+1);
        Sleep(delay_time);
        printf("\t线程%d:job %d delay = %d\n", thrd_num, count, delay_time);
    }
    printf("线程%d 完成\n",thrd_num);

    sem_post(&sem[thrd_num-1]);

    pthread_exit(NULL);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t thread[THREAD_NUMBER];//线程的标识符
    int i = 0, res;

    srand(time(NULL));
    //信号量初始化
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        sem_init(&sem[i], 0, 0);//信号量指针,共享级别:0:线程/else:进程,初始值
    }

    //创建线程
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_create(&thread[i], NULL, thrd_func, (void*)i);
        if (res)
        {
            printf("线程%d创建失败!\n", i);
            exit(res);
        }
    }
    printf("线程全部创建完成!\n等待线程结束、、、\n");

    sem_post(&sem[THREAD_NUMBER - 1]);

    //等待线程结束
    void* thrd_ret;
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_join(thread[THREAD_NUMBER-1-i], &thrd_ret);//阻塞等待结束
        if (!res)
        {
            printf("线程%djoined\n", i);

        }
        else
        {
            printf("线程%djoined 失败!\n");
        }
    }
    for (i = 0; i < THREAD_NUMBER;++i)
    {
        sem_destroy(&sem[i]);
    }
    return 0;
}
条件变量使用:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <semaphore.h>
#include <iostream>

using namespace::std;

#define THREAD_NUMBER 3//线程数

#pragma comment(lib,"pthreadVC2.lib") 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int i = 1;

//线程函数1
void * thread1(void*)
{
    Sleep(1);//必须保证线程2在线程1之前运行到pthread_cond_wait

    pthread_mutex_lock(&mutex);
    cout << "线程1    开始" << endl;
    pthread_cond_signal(&cond);

    cout << "线程1    结束" << endl;

    pthread_mutex_unlock(&mutex);

    return NULL;
}
void *thread2(void *junk)
{
    pthread_mutex_lock(&mutex);
    cout << "线程2    开始" << endl;

    pthread_cond_wait(&cond, &mutex);/*解锁mutex,并等待cond改变*/
    cout << "线程2    重启" << endl;

    cout << "线程2    结束" << endl;

    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main()
{
    pthread_t t_a;
    pthread_t t_b;

    pthread_create(&t_a, NULL, thread1, (void*)NULL);
    pthread_create(&t_b, NULL, thread2, (void*)NULL);

    pthread_join(t_a, NULL);
    pthread_join(t_b, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mutex,然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mutex);,再读取资源。
用这个流程是比较清楚的: block–>unlock–>wait() return–>lock

pthread_cond_broadcast()与pthread_cond_signal()有什么区别?

signal 是唤醒N多 race 中的一个,具体那个被唤醒,依赖于操作系统
broadcast 是唤醒N多reace 的全部
注:broadcast后的非空检查:把资源放入时候后signal或者broadcast,这个时候如果是broadcast,那么可能会唤起很多线程对mutex进行锁竞争,但是必须注意一点,竞争到锁后,对拿到的资源需要进行非空检查,因为别的线程可能已经拿到锁,然后处理完资源把资源清空了


读写锁使用

参考:http://blog.csdn.net/lovecodeless/article/details/24968369

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <semaphore.h>
#include <iostream>
#pragma comment(lib,"pthreadVC2.lib") 

using namespace::std;

pthread_rwlock_t rwlock;

pthread_t readerM, readerN, writerA, writerB;

int data(1);//共享资源

void* readM(void * arg)
{
    while (1)
    {
        pthread_rwlock_rdlock(&rwlock);
        cout << "读者M 读取数据:" << data << endl;
        pthread_rwlock_unlock(&rwlock);
        Sleep(1200);
    }
    return NULL;
}

void* readN(void * arg)
{
    while (1)
    {
        pthread_rwlock_rdlock(&rwlock);
        cout << "读者N 读取数据:" << data << endl;
        pthread_rwlock_unlock(&rwlock);
        Sleep(1200);
    }
    return NULL;
}

void* writeA(void * arg)
{
    while (1)
    {
        pthread_rwlock_wrlock(&rwlock);
        data++;
        cout << "写者A 写入数据:" << data << endl;
        pthread_rwlock_unlock(&rwlock);
        Sleep(2000);
    }
    return NULL;
}

void* writeB(void * arg)
{
    while (1)
    {
        pthread_rwlock_wrlock(&rwlock);
        data++;
        cout << "写者B 写入数据:" << data << endl;
        pthread_rwlock_unlock(&rwlock);
        Sleep(2000);

    }
    return NULL;
}

int main()
{
    pthread_rwlock_init(&rwlock,NULL);

    pthread_create(&readerM, NULL, readM, NULL);
    pthread_create(&readerN, NULL, readN, NULL);
    pthread_create(&writerA, NULL, writeA, NULL);
    pthread_create(&writerB, NULL, writeB, NULL);

    void * res;
    pthread_join(readerM,&res);
    pthread_join(readerN,&res);
    pthread_join(writerA,&res);
    pthread_join(writerB,&res);

    pthread_rwlock_destroy(&rwlock);

    return 0;
}

例题:3个线程,线程id是1,2,3 打印 1、2、3、1、2、3、、
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <semaphore.h>
#include <iostream>

using namespace::std;

#define THREAD_NUMBER 3//线程数
#define REPEAT_NUMBER 5//每个线程中小任务数
#define DELAY_TIME_LEVELS 10.0//小任务之间的最大时间间隔

#pragma comment(lib,"pthreadVC2.lib") 

sem_t sem[THREAD_NUMBER];

void* thred_func(void* thrd_id)
{
    int id = (int)thrd_id; 
    while (1)
    {
        sem_wait(&sem[id]);
        cout << id+1 << "、";
        sem_post(&sem[(id+1)%THREAD_NUMBER]);
    }
    pthread_exit(NULL);
    return NULL;
}

int main()
{
    pthread_t thread[THREAD_NUMBER];
    for (auto i = 0; i < THREAD_NUMBER;++i)
    {
        sem_init(&sem[i], 0, 0);
    }
    int res;
    for (auto i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_create(&thread[i], NULL, thred_func, (void*)i);
        if (res)
        {
            cout << "线程"<<i<<"创建失败!" << endl;
            exit(res);
        }
    }
    sem_post(&sem[0]);

    void * thrd_ret;
    for (auto i = 0; i < THREAD_NUMBER;++i)
    {
        res = pthread_join(thread[i], &thrd_ret);
        if (!res)
        {
            cout << "线程" << i << "joined" << endl;
        }
        else
        {
            cout << "线程" << i << "joined 失败" << endl;
        }
    }

    for (auto i = 0; i < THREAD_NUMBER;++i)
    {
        sem_destroy(&sem[i]);
    }
    return 0 ;
}


用户级线程、内核级线:

线程按照其调度者可以分为用户级线程内核级线程两种。

  • 用户级线程:

我们常用基本就是用户级线程,有关线程管理的工作由应用程序完成,用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时内核意识不到线程的存在。

用户级线程优点:
1. 不需要内核态与用户态切换
2. 可在任意操作系统运行

用户级线程缺点:
1. 一个线程阻塞则所有线程阻塞。
2. 纯粹用户级线程策略中,无法利用多核处理技术,内核一次只把一个进程分配给一个处理器,一个进程中只有一个线程可以执行。

  • 内核级线程:

有关线程管理的工作由内核完成,应用程序没有线程管理代码,只有一个内核提供的应用程序API

内核级线程优点:
1. 内核可将一个进程中的多个线程调度到多个处理器中,内核本身也可以多处理运行。
2. 一个线程阻塞,内核可以调用一个进程中的另一个线程

内核级线程缺点:
1. 线程切换需要到内核的状态切换


多线程使用了多核吗?

SMP:对称多处理系统,内核可以在任何处理器上执行。

  • windows:

windows使用两类与进程相关的对象:进程和线程。

windows支持SMP硬件配置:任何进程的线程,都可以在任何处理器上运行。同一进程中的多个线程可以在多个处理器上同时执行。

  • Linux

Linux提供一种不区分进程和线程的方案,用户级线程被映射到内核级进程上,组成一个用户级进程的多个用户级线程被映射到共享同一组ID的多个linux内核级进程上

Linux中线程和进程没有区别:Linux中通过复制当前的进程的属性可创建一个新进程,新进程被克隆出来,以使它可以共享资源,当两个进程共享虚拟内存时,他们可以被当做是一个进程中的线程。但是没有为线程单独定义数据结构。

linux内核编译时,CONFIG_SMP配置项控制内核是否支持SMP.

现在的内核包从2.4.23以后就没有专门的SMP内核包,在安装Linux系统时,会自动监测,如果检查到了多个CPU或多核,超线程时,会自动安装两个Linux内核,其中一个是带SMP的,在GRUB引导列表里会出现两个内核选择,默认使用SMP引导.


threadlocal,各种锁,synchronized和lock ,
多线程中的wait和sleep区别,notify的作用 ,
线程池对线程的管理方式,包括初始化线程的方法、线程创建后的管理、指派任务的方式
线程池优点、调度处理方式和保护任务队列的方式
无锁编程:
有锁编程的问题与解决方法:阻塞(效率下降)、死锁、优先级反转问题
无锁编程解决单生产者多消费者问题和多生产者多消费者问题
线程安全的接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值