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的时间(时机),以及指针的方面都没有检测出异常)

posted @   xuchaoxin1375  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享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_浮动的认识和使用
点击右上角即可分享
微信分享提示