公司在网站做广告怎么做分录,网站添加二维码,网站设计的可行性分析,网站建设功能覆盖范围文章目录 线程控制 线程控制
线程创建 代码#xff1a; 运行代码#xff1a; 强调一点#xff0c;线程和进程不一样#xff0c;进程有父进程的概念#xff0c;但在线程组里面#xff0c;所有的线程都是对等关系。 错误检查:
传统的一些函数是#xff0c;成功返回0 运行代码 强调一点线程和进程不一样进程有父进程的概念但在线程组里面所有的线程都是对等关系。 错误检查:
传统的一些函数是成功返回0失败返回-1并且对全局变量errno赋值以指示错误。pthreads函数出错时不会设置全局变量errno而大部分其他POSIX函数会这样做。而是将错误代码通过返回值返回pthreads同样也提供了线程内的errno变量以支持其它使用errno的代码。对于pthreads函数的错误建议通过返回值业判定因为读取返回值要比读取线程内的errno变量的开销更小
进程ID和线程ID 在Linux中目前的线程实现是Native POSIX Thread Libaray,简称NPTL。在这种实现下线程又被称为轻量级进程(Light Weighted Process),每一个用户态的线程在内核中都对应一个调度实体也拥有自己的进程描述符(task_struct结构体)。 没有线程之前一个进程对应内核里的一个进程描述符对应一个进程ID。但是引入线程概念之后情况发生了变化一个用户进程下管辖N个用户态线程每个线程作为一个独立的调度实体在内核态都有自己的进程描述符进程和内核的描述符一下子就变成了1N关系POSIX标准又要求进程内的所有线程调用 getpid函数时返回相同的进程ID如何解决上述问题呢 Linux内核引入了线程组的概念。 多线程的进程又被称为线程组线程组内的每一个线程在内核之中都存在一个进程描述符task_struct与之对应。进程描述符结构体中的pid表面上看对应的是进程ID其实不然它对应的是线程ID;进程描述 符中的tgid含义是Thread Group ID,该值对应的是用户层面的进程ID
线程异常 我们之前学到线程一旦异常那么整个进程都会退出那么真的是如此吗 演示 代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;while (true){std::cout name pid: getpid() \n std::endl;int a 100;a / 0;//除0错误sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while (true){std::cout main thread pid: getpid() std::endl;sleep(3);}return 0;
}运行代码 我们发现线程一旦异常确实会影响到整个进程。
结论 1.线程谁先运行与调度器相关 2.随便哪个线程一旦异常都可能导致整个进程整体退出 3.线程在创建并执行的时候线程也需要进行等待的如果主线程不等待也会引起类似于僵尸进程问题导致内存泄漏。
线程等待 已经退出的线程其空间没有被释放仍然在进程的地址空间内。 创建新的线程不会复用刚才退出线程的地址空间。 调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的总结如下:
如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_CANCELED。如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。 参数解释 thread线程id retval输出型参数下面再解释用处 pthread_join默认阻塞等待。 代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;int i 0;while (true){std::cout name runing..... std::endl;sleep(1);if(i 10){break;}}std::cout new thread quit..... std::endl;return nullptr;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);pthread_join(tid,nullptr);std::cout main thread wait done .... main quit! std::endl;return 0;
}运行代码 我们知道pthread_create里面有一个回调函数而回调函数里面有一个返回值我们之前一直返回nullptr 这个返回值一般是给主线程的那么主线程该如何获取到用pthread_join。 pthread_join的第二个参数是输出型参数用来获取放回值的。 代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;int i 0;while (true){std::cout name runing..... std::endl;sleep(1);if (i 10){break;}}std::cout new thread quit..... std::endl;return (void *)10;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);void *ret nullptr;pthread_join(tid, ret);std::cout ret: (long long)ret std::endl;std::cout main thread wait done .... main quit! std::endl;return 0;
}我们运行的时候会这样 我们只要在g后面加-fpermissive即可
g -o mythread mythread.cc -stdc11 -lpthread -fpermissive再运行代码 可以看到我们成功获取到了返回值。 我们不仅仅只能返回变量我们还能返回其它内容。 代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;int i 0;int *data new int[10];while (true){std::cout name runing..... std::endl;sleep(1);data[i] i;if (i 10){break;}}std::cout new thread quit..... std::endl;return (void *)data;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);void *ret nullptr;pthread_join(tid, ret);int *data (int *)ret;for (int i 0; i 10; i){std::cout data[i] ;}std::cout std::endl;std::cout main thread wait done .... main quit! std::endl;return 0;
}运行结果 线程终止 如果需要只终止某个线程而不终止整个进程,可以有三种方法:
从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。线程可以调用pthread_ exit终止自己。一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。 能不能用exit终止线程呢
代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;int i 0;int *data new int[10];while (true){std::cout name runing..... std::endl;sleep(1);data[i] i;if (i 10){break;}}std::cout new thread quit..... std::endl;exit(10);return (void *)data;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);void *ret nullptr;pthread_join(tid, ret);int *data (int *)ret;for (int i 0; i 10; i){std::cout data[i] ;}std::cout std::endl;std::cout main thread wait done .... main quit! std::endl;return 0;
}运行结果 我们发现整个进程都被终止了因为exit是终止进程的绝对不要用exit终止线程。 那么我们如何终止新线程而不影响main线程呢 pthread_exit()OS提供的终止线程的函数 参数retval就是之前的返回值。 代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;int i 0;int *data new int[10];while (true){std::cout name runing..... std::endl;sleep(1);data[i] i;if (i 10){break;}}std::cout new thread quit..... std::endl;pthread_exit((void*)data);
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);void *ret nullptr;pthread_join(tid, ret);int *data (int *)ret;for (int i 0; i 10; i){std::cout data[i] ;}std::cout std::endl;std::cout main thread wait done .... main quit! std::endl;return 0;
}运行结果 我们看到线程终止成功。
线程取消 代码
#include iostream
#include pthread.h
#include unistd.hvoid *threadRoutine(void *arg)
{const std::string name (char *)arg;while (true){std::cout name runing..... std::endl;sleep(1);}std::cout new thread quit..... std::endl;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);int count 3;while (true){std::cout main thread pid: getpid() std::endl;if(count 5) break;sleep(2);}pthread_cancel(tid);std::cout pthread cancle tid: tid std::endl; void *ret nullptr;pthread_join(tid, ret);std::cout ret: (long long)ret std::endl;std::cout main thread wait done .... main quit! std::endl;sleep(5);return 0;
}运行结果 我们看到最后main线程确实等待了5秒 然后退出了。 我们看到其中tid为啥这么大呢之后再讲解。 而我们看到线程被取消我们join的时候退出码是-1. 而-1其实是
线程ID的探索 我们之前看到线程ID是一个很大的值 格式化输出线程ID 代码
#include iostream
#include pthread.h
#include unistd.h
#include cstdiovoid *threadRoutine(void *arg)
{const std::string name (char *)arg;while (true){std::cout name runing..... std::endl;sleep(1);}std::cout new thread quit..... std::endl;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);printf(%u,%p\n,tid,tid);int count 3;while (true){std::cout main thread pid: getpid() std::endl;if(count 5) break;sleep(2);}pthread_cancel(tid);std::cout pthread cancle tid: tid std::endl; void *ret nullptr;pthread_join(tid, ret);std::cout ret: (long long)ret std::endl;std::cout main thread wait done .... main quit! std::endl;sleep(5);return 0;
}运行结果 我们看到线程ID值很大tid的本质是一个地址。 为什么tid不用Linux中的LWP呢 因为目前用的不是Linux自带的创建线程的接口我们用的是pthread库中的接口。 我们知道线程共享进程的地址空间 但是线程有自己独立的栈结构那么如何保证栈区是每一个线程独占的呢----原本的栈给main线程使用而其余线程把共享区当做栈区。所以每个线程的tid就是自己栈区的起始地址 见一见 pthread库时通过clone做到上面的那点。 那么我们如何获取线程的id呢 代码
#include iostream
#include pthread.h
#include unistd.h
#include cstdiovoid *threadRoutine(void *arg)
{const std::string name (char *)arg;while (true){std::cout name runing..... id: pthread_self() std::endl;sleep(1);}std::cout new thread quit..... std::endl;
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);int count 3;while (true){std::cout main thread id: pthread_self() std::endl;if(count 5) break;sleep(2);}void *ret nullptr;pthread_join(tid, ret);std::cout ret: (long long)ret std::endl;std::cout main thread wait done .... main quit! std::endl;sleep(5);return 0;
}运行代码 我们看到我们获取到了不同的线程id
大部分线程的代码是共享的 一个小实验 代码
#include iostream
#include pthread.h
#include unistd.h
#include cstdioint g_val 0;
void *threadRoutine(void *arg)
{const std::string name (char *)arg;while(true){std::cout name g_val: g_val g_val g_val std::endl;g_val;sleep(1);}}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while(true){std::cout main thread g_val: g_val g_val g_val std::endl;sleep(1);}return 0;
}运行结果 我们看到g_val被大家所共享大家都可以看到g_val一个线程对其进程改变其它线程都看的到。 那么如果线程想要自己是私有的变量呢该如何 只要在变量前加__thread即可。 代码
#include iostream
#include pthread.h
#include unistd.h
#include cstdio__thread int g_val 0;
void *threadRoutine(void *arg)
{while(true){std::cout (char*)arg : g_val : g_val std::endl;g_val;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while(true){std::cout main thread: g_val : g_val std::endl;sleep(1);}return 0;
}运行代码 这里运行的时候是并行执行的所以会看不清但是我们也能看到两个变量的地址不一样的。
__thread修饰全局变量带来的结果就是让每一个线程各自拥有一个全局变量----线程的就不存储。
我们之前学过进程替换如果线程进行进程替换会如何 代码
#include iostream
#include pthread.h
#include unistd.h
#include cstdio__thread int g_val 0;
void *threadRoutine(void *arg)
{execl(/bin/ls,ls,nullptr);while(true){std::cout (char*)arg : g_val : g_val std::endl;g_val;sleep(1);}}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while(true){std::cout main thread: g_val : g_val std::endl;sleep(1);}return 0;
}运行结果 我们看到ls确实被执行了但是整个进程的代码都被替换掉了。
分离线程 默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。 如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。
测试代码
#include iostream
#include pthread.h
#include unistd.h
#include cstdio
#include cerrno
#include cstring__thread int g_val 0;
void *threadRoutine(void *arg)
{pthread_detach(pthread_self());while(true){std::cout (char*)arg : g_val : g_val std::endl;g_val;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while(true){std::cout main thread: g_val : g_val std::endl;sleep(1);break;}int n pthread_join(tid,nullptr);std::cout n: n errstring: strerror(n) std::endl;return 0;
}运行结果 我们看到join异常进程直接退出。
所以线程分离后线程异常也会影响整个进程
C语言提供的线程而语言级别的线程库必须调用原生线程库----本质是对原生线程库的封装 代码 运行 进程线程间的互斥相关背景概念 临界资源多线程执行流共享的资源就叫做临界资源 临界区每个线程内部访问临界自娱的代码就叫做临界区 互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用 原子性后面讨论如何实现不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成
如果多个线程访问同一个全局变量并对它进行数据计算多线程会互相影响吗 测试代码 抢票代码
#include iostream
#include thread
#include pthread.h
#include unistd.h
#include cstdio
#include cerrno
#include cstringint tickets 10000;
void *GetTickets(void *args)
{while (true){if (tickets 0){usleep(1000);printf(%p : %d\n, pthread_self(), tickets);tickets--;}else{break;}}return nullptr;
}int main()
{pthread_t t1;pthread_t t2;pthread_t t3;pthread_create(t1, nullptr, GetTickets, nullptr);pthread_create(t2, nullptr, GetTickets, nullptr);pthread_create(t3, nullptr, GetTickets, nullptr);pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);return 0;
}运行结果 我们发现票抢到-1了这肯定是错的 每次运行的结果都不一定一样 所以tickets在并发访问的时候导致了我们数据不一致的问题。之后再解决这个歌问题。