linux_pthread多进程多线程试验
试验代码
common_fun.c
#ifndef common_func #define common_func #include "prints.h" #include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <unistd.h> #include <pthread.h> #include <stdbool.h> #include <string.h> #include <errno.h> #include <time.h> void send_char_by_char(char *str, int pipefd_1) { // char *str = "msg!"; int len = 0; // write(pipefd[1], str, strlen(str)); dprint(len = strlen(str)); for (int i = 0; i < len; i++) { // 我们可以让写入管道的操作以每秒一个字符的速度写入,观测子进程是否会因此阻塞(读取字符的速度不会超过每秒一个!) // sleep(1); extern void msleep(int tms); write(pipefd_1, str + i, 1); msleep(200); } close(pipefd_1); /* Reader will see EOF */ } void msleep(int tms) { struct timeval tv; tv.tv_sec = tms / 1000; tv.tv_usec = (tms % 1000) * 1000; select(0, NULL, NULL, NULL, &tv); }
prints.h
// 数值调试宏 #ifndef CXXU #define CXXU 1 // 修改sizeint来指定打印宽度:(注意,必须以字符串的形式修改,(数字要包裹双引号)) // 负数,就是左对齐 #define sizeint__ "25" #define sizestr__ "%" sizeint__ "s" #define dprint(expr) printf(sizestr__ " = %d @%%d\n", #expr, expr) #define ldprint(expr) printf(sizestr__ " = %ld @%%ld\n", #expr, expr) #define cprint(expr) printf(sizestr__ " = %c @%%c\n", #expr, expr) #define sprint(expr) printf(sizestr__ " = %s @%%s\n", #expr, expr) #define gprint(expr) printf(sizestr__ " = %g\n", #expr, expr) #define fprint(expr) printf(sizestr__ " = %f\n", #expr, expr) // #define sprint(expr) printf("\t@sprint"#expr " = %s\n", expr) // #define sprint(expr) printf(expr) #define sprintln(expr) printf(expr "\n") #define pre_print(expr) printf(expr) // 直接传递变量给pprint(取地址操作包含在了宏中) #define pprint(expr) printf(sizestr__ " = %p &var%%p\n", "&" #expr, &expr) // 直接打印传入的地址(指针变量) #define pprinta(expr) printf(sizestr__ " = %p %%p (pointer:" #expr ")\n", #expr, expr) // extern void func(); // extern int multiply(int a, int b); // extern char *str_multiplier; #endif
main.c
// include Posix threads库(Pthread) #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include "../common_fun.c" /* Compiling on Linux On Linux, programs that use the Pthreads API should be compiled using cc -pthread. SYNOPSIS top #include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); Compile and link with -pthread. */ /* man 7 pthreads */ /* Pthreads function return values Most pthreads functions return 0 on success, and an error number on failure. The error numbers that can be returned have the same meaning as the error numbers returned in er‐ rno by conventional system calls and C library functions. Note that the pthreads functions do not set errno. For each of the pthreads functions that can return an error, POSIX.1-2001 specifies that the function can never fail with the error EINTR. Thread IDs Each of the threads in a process has a unique thread identifier (stored in the type [pthread_t]). This identifier is returned to the caller of pthread_create(3), and a thread can obtain its own thread identifier using pthread_self(3). Thread IDs are guaranteed to be unique only within a process. (In all pthreads functions that accept a thread ID as an argument, that ID by definition refers to a thread in the same process as the caller.) The system may reuse a thread ID after a terminated thread has been joined, or a detached thread has terminated. POSIX says: "If an application attempts to use a thread ID whose lifetime has ended, the behavior is undefined." */ /* mutex:mutual exlustion mutexes:mutex+es(plurality of mutex) */ /* Linux implementations of POSIX threads Over time, two threading implementations have been provided by the GNU C library on Linux: LinuxThreads This is the original Pthreads implementation. Since glibc 2.4, this implementation is no longer supported. NPTL (Native POSIX Threads Library) This is the modern Pthreads implementation. By comparison with LinuxThreads, NPTL provides closer conformance to the requirements of the POSIX.1 specification and better performance when creating large numbers of threads. NPTL is available since glibc 2.3.2, and requires features that are present in the Linux 2.6 kernel. Both of these are so-called 1:1 implementations, meaning that each thread maps to a kernel scheduling entity. Both threading implementations employ the Linux clone(2) system call. In NPTL, thread synchronization primitives (mutexes, thread joining, and so on) are implemented using the Linux futex(2) system call. */ /* pthread_create() */ /* PTHREAD_CREATE(3) Linux Programmer's Manual PTHREAD_CREATE(3) NAME pthread_create - create a new thread SYNOPSIS #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); Compile and link with -pthread. DESCRIPTION The pthread_create() function starts a new thread in the calling process. The new thread starts execution by invoking start_routine(); arg is passed as the sole argument of start_routine(). The new thread terminates in one of the following ways: * It calls pthread_exit(3), specifying an exit status value that is available to another thread in the same process that calls pthread_join(3). * It returns from start_routine(). This is equivalent to calling pthread_exit(3) with the value supplied in the return statement. * It is canceled (see pthread_cancel(3)). * Any of the threads in the process calls exit(3), or the main thread performs a return from main(). This causes the termination of all threads in the process. The attr argument points to a pthread_attr_t structure whose contents are used at thread creation time to determine attributes for the new thread; this structure is initialized using pthread_attr_init(3) and related functions. If attr is NULL, then the thread is created with default attributes. Before returning, a successful call to pthread_create() stores the ID of the new thread in the buffer pointed to by thread; this identifier is used to refer to the thread in subsequent calls to other pthreads functions. */ int val = 0; //全局变量val int v = 0; int *pv = &v; void *runner(void *param); //测试函数1的声明 void *runner1(void *param); //测试函数2的声明 /* 定义主函数 */ int main(int argc, char *argv[]) { // 进程号变量 int pid; /* Thread IDs Each of the threads in a process has a unique thread identifier (stored in the type pthread_t). This identifier is returned to the caller of pthread_create(3), and a thread can obtain its own thread identifier using pthread_self(3). */ pthread_t tid, tid1; // tid:(posix) thread id pthread_attr_t attr, attr1; //声明两个线程属性变量 // printf("%ld\n", sizeof(attr)); pid = fork(); if (pid == 0) { /* int pthread_attr_init(pthread_attr_t *__attr) Initialize thread attribute *ATTR with default attributes */ /* man 手册中有一个很长的示例代码 */ pthread_attr_init(&attr); /* int pthread_create( pthread_t *__restrict__ __newthread, const pthread_attr_t *__restrict__ __attr, void *(*__start_routine)(void *), void *__restrict__ __arg) */ /* The [thread] argument [points] to a buffer of type pthread_t [into] which the unique identifier for this thread is copied before pthread_create() returns. This identifier can be used in later Pthreads calls to refer to the thread. */ /* The [attr] argument is a [pointer] to a [pthread_attr_t] object that specifies various attributes for the new thread. If attr is specified as [NULL], then the thread is created with various [default attributes], and this is what we’ll do in most of the example programs in this book. */ /* 令其将全局变量val+5,并在线程内查看效果 */ pthread_create(&tid, &attr, runner, NULL); printf("This is the child. The value is %d\n", val); //输出结果为:This is the child. The value is /* 创建第二个线程,令其将全局变量val+4; */ pthread_attr_init(&attr1); pthread_create(&tid1, &attr1, runner1, NULL); printf("This is the other thread in child. the value is %d\n", val); //输出结果为:This is the other thread in child. the value is sprintln("子进程:主线程被子进程阻塞中..等待子线程结束并(waiting join)子线程..."); /* 回收线程 */ /* NAME pthread_join - join with [a terminated] thread // 有点儿像进程中wait()的味道(父进程wait已经exit的子进程) //主线程thread_join 已经thread_exit()的次生线 SYNOPSIS #include <pthread.h> int pthread_join(pthread_t thread, void **retval); Compile and link with -pthread. DESCRIPTION The pthread_join() function waits for the thread [specified by thread] to terminate. If that thread has already terminated, then pthread_join() returns immediately. The thread specified by thread must be joinable. If retval is not NULL, then pthread_join() copies the exit status of the target thread (i.e., the value that the target thread supplied to pthread_exit(3)) into the location pointed to by retval. If the target thread was canceled, then PTHREAD_CANCELED is placed in the location pointed to by retval. If [multiple threads] [simultaneously] try to join with [the same thread], //非主线程也可以join 其他线程 the results are undefined. If the thread calling pthread_join() is canceled, then the target thread will remain joinable (i.e., it will not be detached). RETURN VALUE On success, pthread_join() returns 0; on error, it returns an error number. */ pthread_join(tid, NULL); sprintln(""); sprintln("t1:joined!"); pthread_join(tid1, NULL); sprintln("t2:joined!"); sprintln("所有线程join完毕.!"); sprintln(""); sprintln("子进程(by forked)将结束"); sprintln("t1,t2都已经结束,再次在子进程中检测val:"); dprint(val); *pv = 9; sprintln("检测指针:"); pprinta(&val); // 测试exit() // exit(EXIT_SUCCESS); } else { sprintln("主进程在wait子进程资源...."); // printf("This is the parent. The value is %d\n", val); //输出结果为:This is the parent. The value is msleep(600); // sprintln("!!!主进程对val做减法"); // v -= 13.5; // dprint(v); wait(NULL); // sleep(2); printf("This is the parent. The value is %d\n", val); //输出结果为:This is the parent. The value is dprint(v); sprintln("主进程中检测val指针"); pprinta(&val); sprintln("主进程结束"); } printf("@@@val=%d\n", val); } void *runner(void *param) { sprintln(""); printf("I am thread 1\n"); sprintln("尝试对val+5"); val += 5; *pv += 5; printf("t1:value in runner before sleep: %d\n", val); //输出结果为:value in runner before sleep: printf("t1:sleep..."); sleep(2); sprintln("t1:睡眠结束,检测t2是否影响到val"); printf("value in runner after sleep: %d\n", val); //输出结果为:value in runner after sleep: dprint(v); sprintln(""); pthread_exit(0); } void *runner1(void *param) { sprintln(""); printf("I am thread 2\n"); sprintln("t2:尝试对val+4"); val += 4; *pv += 4; sprintln("t2:检测t2的操作效果"); dprint(val); dprint(v); sprintln(""); pthread_exit(0); }
实验效果
遗留困惑
• 在线程的试验中,对全局变量的修改在主进程中没有看看到效果,而在创建线程的子进程中却可以看到线程的修改效果,令人颇为困惑.(从查看val的时间(时机),以及指针的方面都没有检测出异常)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2023-08-15 LA@2@1@线性方程组和简单矩阵方程有解判定定理
2023-08-15 java_一句话描述多态.对比普通变量和引用变量
2023-08-15 c++常量成员函数/编写规则 实例
2022-08-15 css_float_浮动的认识和使用