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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值