目录

文章目录

  • 目录
  • pthread 线程库
  • TCB 结构体
  • 线程的生命周期管理
    • 线程的合并与分离
    • pthread_create() 创建线程
    • pthread_join() 合并线程
    • pthread_exit() 线程主动退出
    • pthread_detach() 分离线程
  • 线程的属性
    • 线程分离属性
    • LWP 绑定属性
    • CPU 亲和性属性
    • 调度属性
  • 多核平台并行编程示例
  • 多线程安全与多线程同步
    • 互斥锁(Mutex)
      • pthread_mutex_init()
      • pthread_mutex_lock()
      • pthread_mutex_unlock()
    • 条件变量(Condition Variable)
      • pthread_cond_init()
      • pthread_cond_wait()
      • pthread_cond_signal()
      • pthread_cond_broadcast()
    • 互斥锁和条件变量配合使用
  • 线程非安全标准库函数

pthread 线程库

pthread(POSIX Threads)是一套符合 POSIX(Portable Operating System Interface,可移植操作系统接口)的 User Thread 操作 API 标准,定义了一组线程相关的函数(60 多个)和数据类型。pthread API 可以用于不同的操作系统上,因此被称为可移植的线程 API。

在版本较新的 Linux Kernel 中,pthread API 的具体实现是 NPTL(Native POSIX Thread Library)。为了方便描述,在后文中我们使用 pthread 来统称。

  • 实现源码:https://github.com/lattera/glibc/blob/master/nptl/pthread_create.c
  • 文档:https://docs.oracle.com/cd/E19253-01/819-7051/attrib-74380/index.html

pthread 线程库围绕 struct pthread 提供了一系列的接口,用于完成 User Thread 创建、调度、销毁等一系列管理。

TCB 结构体

在 pthread 中,使用 TCB(Thread Control Block,线程控制块)来存储 User Thread 的所有信息,TCB 的体量会比 PCB 小非常多。对应的 pthread 结构体如下:

// glibc/nptl/descr.h/* Thread descriptor data structure.  */
struct pthread {struct pthread *self;           // 指向自身的指针struct __pthread_internal_list *thread_list;  // 线程列表,指向线程列表的指针,用于实现线程池;void *(*start_routine)(void*);  // 线程的入口函数,由 pthread_create() 函数传入;void *arg;                      // 线程的入口函数参数,由 pthread_create() 函数传入;void *result;                   // 线程的返回值,由线程的入口函数返回;pthread_attr_t *attr;           // 线程的属性,包括栈保护区大小、调度策略等,由 pthread_create() 函数传入;pid_t tid;                      // 线程的唯一标识符,由 Kernel 分配;struct timespec *waiters;       // 等待的时间戳size_t guardsize;               // 栈保护区大小int sched_policy;               // 调度策略struct sched_param sched_params;// 调度参数void *specific_1stblock;        // 线程私有数据的第一个块struct __pthread_internal_slist __cleanup_stack;  // 清理函数栈struct __pthread_mutex_s *mutex_list;  // 线程持有的互斥锁列表struct __pthread_cond_s *cond_list;    // 线程等待的条件变量列表unsigned int detach_state:2;     // 线程分离状态,包括分离和未分离两种;unsigned int sched_priority:30;  // 线程的调度优先级unsigned int errno_val;          // 线程的错误码
};

线程的生命周期管理

线程的合并与分离

对于 User Thread 的生命周期管理,首先要明确线程合并和线程分离的概念。

线程的合并与分离是指在多线程程序中,对于已经创建的线程进行结束和回收资源的 2 种操作方式。

  • 线程的合并:指等待某个线程结束后,主线程再继续执行资源回收。
  • 线程的分离:指线程结束后会自动释放资源,不需要等待主线程回收。

需要注意的是,线程的合并和分离操作都必须在目标线程执行结束之前进行,并且必须二选一,否则会导致内存泄露甚至崩溃。

pthread_create() 创建线程

函数作用:用于创建一条新的对等线程,并指定线程的入口函数和参数。pthread 库就会为 User Thread 分配 TCB、PC(程序计数器)、Registers(寄存器)和 Stack(栈)等资源。并将其加入到 Thread Queue 中等待执行。直到 User Thread 被调度到 CPU 时,开始执行线程入口函数。

函数原型

  • thread 参数:是一个 pthread_t 类型指针,用于存储 TID。
  • attr 参数:是一个 pthread_attr_t 类型指针,用于指定线程的属性,通常为 NULL。
  • start_routine 参数:线程入口函数,是一个 void* 类型函数指针(或直接使用函数名)。线程入口函数必须是一个 static 静态函数或全局函数,因为 pthread 会把线程入口函数的返回值传递到 pthread_join() 中,所以需要能够找到它。
  • arg 参数:线程参数,是一个 void* 类型参数。
  • 函数返回值
    • 成功:返回 0;
    • 失败:返回 -1;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

pthread_join() 合并线程

函数作用:执行线程合并。阻塞当前的主线程,直到指定线程执行结束,然后获得线程的执行结果,并释放线程的资源。

函数原型

  • thread 参数:指定等待的 TID。
  • retval:是一个指向指针的指针类型,用于存储线程的结果返回。
int pthread_join(pthread_t thread, void **retval);

pthread_exit() 线程主动退出

函数作用:线程主动终止自己,返回结果到 pthread_join()。需要注意的是,Main Thread 不应该调用 pthread_exit(),这样会退出整个 User Process。

函数原型

  • retval:是一个指针类型,用于存储退出码。如果不需要返回值,则设置为 NULL。
void pthread_exit(void *retval);

pthread_detach() 分离线程

函数作用:执行线程分离。将指定的线程标记为 “可分离的“,表示该线程在执行结束后会自动释放资源(由资源自动回收机制完成),无需等待主线程回收。另一方面,这也意味这主线程无法获得线程的返回值。

函数原型

  • thread 参数:指定 TID。
int pthread_detach(pthread_t thread);

线程的属性

可以在 pthread_create() 新建线程时,直接指定线程的属性,也可以更改已经存在的线程的属性,包括:

  • 线程分离属性;
  • LWP 绑定属性;
  • CPU 亲和性属性;
  • 调度属性;
  • 等等。
// 定义一个 pthread attribute 实例。
pthread_attr_t attr;// 初始化一个 pthread attribute 实例。
int pthread_attr_init(pthread_attr_t *attr);
// 清除一个 pthread attribute 实例。
int pthread_attr_destory(pthread_attr_t *attr);

线程分离属性

线程分离属性,即:将线程设定为 “可分离的"。

函数原型

  • attr:指定一个 pthread attribute 实例。
  • detachstate:指定 attr 的分离属性:
    • PTHREAD_CREATE_DETACHED:指示线程是分离的。
    • PTHREAD_CREATE_JOINABLE:默认属性,指示线程是合并的,需要主线程调用 pthread_join() 来等待并释放资源。
pthread_attr_setdetachstat(pthread_attr_t *attr, int detachstate);

设定属性后不需要再通过 pthread_detach() 重复设定。

LWP 绑定属性

POSIX 标准引入了 “线程竞争域“ 的概念,即:User Threads 对 CPU 资源发起竞争的范围,并要求至少要实现下列 2 种范围之一:

  1. PTHREAD_SCOPE_PROCESS:User Threads 在 User Process 范围内竞争 CPU 资源。
  2. PTHREAD_SCOPE_SYSTEM:User Threads 在 System 范围内竞争 CPU 资源。

相应的,pthread API 库也提供了 pthread_attr_setscope() 接口来设定 User Threads 的竞争范围。但是,实际上 Linux NPTL 只实现了 PTHREAD_SCOPE_SYSTEM 这一种方式。

具体而言就是 LWP(Light Weight Process)的实现。在还没有 pthread 线程库的早期版本的 Linux 中,只有 Kernel Thread 的概念,User Process 只能通过 kthread_crearte SCI(系统调用接口)来创建 Thread。但这种方式显然会存在 User Space(User Process)和 Kernel Space(Kernel Thread)之间频繁的切换。

为了解决这个问题,POSIX 标准引入了 User Thread 和 LWP 的概念,最早在 Solaris 操作系统中实现。之所以要同时引入 LWP 的目的是为了让实现 User Thread 的 pthread API 接口能够在不同的操作系统中保持良好的兼容性。

而 Linux NPTL 则将 LWP 作为 User Thread 和 Kernel Thread 之间建立映射关系的桥梁,并让 User Threads 能够竞争全局的 CPU 资源,以此来发挥多核处理器平台的并行优势。

当调用 pthread_create() 新建多个 User Threads 时,Kernel 会为这些 User Threads 创建少量的 LWPs,并建立 M:N 的映射关系。这个映射过程是由 Kernel 完成的,开发者无法手动干预。

CPU 亲和性属性

在 Kernel 中,LWP 同样作为可调度单元,与 kthread_create() 创建的 Kernel Thread 一般,可以被 Kernel Scheduler 识别并根据调度策略调度到不同的 CPU 上执行。

默认情况下,User Thread 依靠 LWP 的可调度能力,会被 Kernel 尽力而为的分配到多个不同的 CPU cores 上执行,以达到负载均衡。但这种分配是随机的,不保证 User Thread 最终在那个 Core 上执行。

相对的,可以通过修改 User Threads 的 CPU 亲和性属性让它们在指定的 cpuset 中竞争。

函数原型

  • attr:指定一个 pthread attribute 实例。
  • cpusetsize:指示 cpuset 实例的大小。
  • cpuset:指示 cpuset 实例,通过 <sched.h> 中定义的函数进行初始化和操作。
int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset);

调度属性

User Thread 的调度属性有 3 类,分别是:调度算法、调度优先级、调度继承权。

调度算法,函数原型

  • attr:指定一个 pthread attribute 实例。
  • policy:指定调度算法:
    • SCHED_OTHER:Linux 私有,默认采用,用于非实时应用程序。
    • SCHED_FIFO(先进先出):POSIX 标准,用于实时应用程序。
    • SCHED_RR(轮询):POSIX 标准,用于实时应用程序。
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

调度优先级,函数原型:只在 SCHED_FIFO 和 SCHED_RR 等实时调度算法中生效,User Process 需要以 root 权限运行,且需要显式放弃父线程的继承权。

  • attr:指定一个 pthread attribute 实例。
  • param:指向了一个 sched_param 结构体,其中 sched_priority 字段用于指定优先值,范围 1~99。
struct sched_param {int sched_priority;char __opaque[__SCHED_PARAM_SIZE__];
};int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);

调度继承权,函数原型:子线程是否继承父线程的调度算法和调度优先级。

  • attr:指定一个 pthread attribute 实例。
  • inheritsched
    • PTHREAD_EXPLICIT_SCHED:不继承。
    • PTHREAD_INHERIT_SCHED:继承,默认。
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);

多核平台并行编程示例

实践多线程的目的往往在于提升应用程序的执行性能,通常有并发和并行这 2 种方式:

  1. 并发程序:并发指在同一时间段内,多线程在同一个 CPU 上执行。并发程序不强制要求 CPU 具备多核计算能力,只要求多个线程在同一个 Core 上进行 “分时轮询” 处理,以此在宏观上实现多线程同时执行的效果。并发程序的执行通常是不确定的,这种不确定性来源于资源之间的相关依赖和竞态条件,可能导致执行的线程间相互等待(阻塞)。并发程序通常是有状态的(非幂等性)。

  2. 并行程序:并行指在同一时刻内,多线程在不同的 CPU core 上同时执行。并行程序会强制要求 CPU 具备多核计算能力,并行程序的每个执行模块在逻辑上都是独立的,即线程执行时可以独立地完成任务,从而做到同一时刻多个指令能够同时执行。并行程序通常是无状态的(幂等性)。

示例程序:

#define _GNU_SOURCE // 用于启用一些非标准的、GNU C 库扩展的特性,例如:<sched.h> 中的 CPU_ZERO 和 CPU_SET 函数。#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <sched.h>#define THREAD_COUNT 12  // 12 个对等线程void show_thread_sched_policy_and_cpu(int threadno)
{int cpuid;int policy;struct sched_param param;cpuid = sched_getcpu();pthread_getschedparam(pthread_self(), &policy, &param);printf("Thread %d is running on CPU %d, ", threadno, cpuid);switch (policy){case SCHED_OTHER:printf("SCHED_OTHER\n", threadno);break;case SCHED_RR:printf("SCHDE_RR\n", threadno);break;case SCHED_FIFO:printf("SCHED_FIFO\n", threadno);break;default:printf("UNKNOWN\n");}
}void *thread_func(void *arg)
{int i, j;long threadno = (long)arg;printf("thread %d start\n", threadno);sleep(1);show_thread_sched_policy_and_cpu(threadno);for (i = 0; i < 10; ++i){// 适当调整执行时长for (j = 0; j < 10000000000; ++j){}}printf("thread %d exit\n", threadno);return NULL;
}int main(int argc, char *argv[])
{long i;int cpuid;cpu_set_t cpuset;pthread_attr_t attr[THREAD_COUNT];pthread_t pth[THREAD_COUNT];struct sched_param param;// 初始化线程属性for (i = 0; i < THREAD_COUNT; ++i)pthread_attr_init(&attr[i]);// 调度属性设置for (i = 0; i < THREAD_COUNT / 2; ++i){param.sched_priority = 10;pthread_attr_setschedpolicy(&attr[i], SCHED_FIFO);pthread_attr_setschedparam(&attr[i], &param);pthread_attr_setinheritsched(&attr[i], PTHREAD_EXPLICIT_SCHED);}for (i = THREAD_COUNT / 2; i < THREAD_COUNT; ++i){param.sched_priority = 20;pthread_attr_setschedpolicy(&attr[i], SCHED_RR);pthread_attr_setschedparam(&attr[i], &param);pthread_attr_setinheritsched(&attr[i], PTHREAD_EXPLICIT_SCHED);}// CPU 亲和性属性设置,使用 cpuset(0,1)。for (i = 0; i < THREAD_COUNT; ++i){pthread_create(&pth[i], &attr[i], thread_func, (void *)i);CPU_ZERO(&cpuset);cpuid = i % 2;CPU_SET(cpuid, &cpuset);pthread_setaffinity_np(pth[i], sizeof(cpu_set_t), &cpuset);}for (i = 0; i < THREAD_COUNT; ++i)pthread_join(pth[i], NULL);// 清理线程属性for (i = 0; i < THREAD_COUNT; ++i)pthread_attr_destroy(&attr[i]);return 0;
}
  • CPU 调度亲和性效果。

  • User Thread 和 LWP(12 + 1)绑定关系与调度策略效果。

$ ps -eLo pid,ppid,tid,lwp,nlwp,class,rtprio,ni,pri,psr,pcpu,policy,stat,comm | awk '$7 !~ /-/{print $0}'PID  PPID   TID   LWP NLWP CLS RTPRIO  NI PRI PSR %CPU POL STAT COMMAND
26031 24641 26032 26032   13 FF      10   -  50   0  0.0 FF  Rl+  test1
26031 24641 26033 26033   13 FF      10   -  50   1  0.0 FF  Rl+  test1
26031 24641 26034 26034   13 FF      10   -  50   0  0.0 FF  Rl+  test1
26031 24641 26035 26035   13 FF      10   -  50   1  0.0 FF  Rl+  test1
26031 24641 26036 26036   13 FF      10   -  50   0  0.0 FF  Rl+  test1
26031 24641 26037 26037   13 FF      10   -  50   1  0.0 FF  Rl+  test1
26031 24641 26038 26038   13 RR      20   -  60   0 33.3 RR  Rl+  test1
26031 24641 26039 26039   13 RR      20   -  60   1 33.3 RR  Rl+  test1
26031 24641 26040 26040   13 RR      20   -  60   0 33.3 RR  Rl+  test1
26031 24641 26041 26041   13 RR      20   -  60   1 33.2 RR  Rl+  test1
26031 24641 26042 26042   13 RR      20   -  60   0 33.2 RR  Rl+  test1
26031 24641 26043 26043   13 RR      20   -  60   1 33.3 RR  Rl+  test1
  • PID:进程 ID,唯一标识一个进程。
  • PPID:父进程 ID,标识当前进程的父进程。
  • TID:线程 ID,标识一个线程,与 LWP ID 一致(一一对应)。
  • LWP:LWP ID,是内核调度实体,多个 LWP 可以属于同一个进程,但它们的调度是相互独立的。
  • NLWP:进程的 LWP 数。
  • CLS:调度算法。
  • RTPRIO:实时优先级,仅适用于实时调度策略。
  • NI:Nice 值,越小表示越高的优先级。
  • PRI:优先级,与 NI 的值相关。
  • PSR:进程或线程所绑定的处理器编号。
  • %CPU:进程或线程的 CPU 使用率。
  • POL:进程或线程的调度策略。
  • STAT:进程或线程的状态。
  • COMMAND:进程或线程的命令名或可执行文件名。

多线程安全与多线程同步

多线程安全(Multi-Thread Safe),就是在多线程环境中,多个线程在同一时刻对同一份共享数据(Shared Resource,e.g. 寄存器、内存空间、全局变量、静态变量 etc.)进行写操作(读操作不会涉及线程安全的问题)时,不会出现数据不一致。

为了确保在多线程安全,就要确保数据的一致性,即:线程安全检查。多线程之间通过需要进行同步通信,以此来保证共享数据的一致性。

pthread 库提供了保证线程安全的方式:

  1. 互斥锁(Mutex):是一种线程安全机制,为共享数据加上一把锁,拥有锁的线程,才可以访问共享数据。以此保护共享数据不被多个线程同时访问。
  2. 条件变量(Condition Variable):是一种线程同步机制,用于判断线程是否满足了特定的竞争条件(Race Condition)。只有满足条件的线程,才可以获得互斥锁,以此来避免死锁的情况。

需要注意的是,线程安全检查的实现会带来一定的系统开销。

互斥锁(Mutex)

pthread_mutex_init()

函数作用:用于初始化一个互斥锁实体。

函数原型

  • mutex 参数:pthread_mutex_t 类型指针,用于指定要初始化的互斥锁。
  • attr 参数:pthread_mutexattr_t 类型指针,用于指定互斥锁的属性,例如:递归锁、非递归锁等,通常为 NULL。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

pthread_mutex_lock()

函数作用:User Thread 用于获取互斥锁。如果互斥锁已被 Other User Thread 获得,则当前 User Thread 会阻塞。

函数原型

  • mutex 参数:pthread_mutex_t 类型指针,用于指定要获取的互斥锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_unlock()

函数作用:User Thread 用于释放互斥锁,互斥锁重回可用状态。如果当前 User Thread 并没有锁,则该函数可能会产生未定义行为。

函数原型

  • mutex 参数:pthread_mutex_t 类型指针,用于指定要释放的互斥锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);

条件变量(Condition Variable)

互斥锁和条件变量都是多线程同步的工具,但是它们的作用不同:

  • 互斥:互斥锁可以保护共享资源的访问,防止多个线程同时修改共享资源,但是它无法告知其他线程何时可以安全地访问共享资源,有可能导致死锁的发生。

举例来说,存在全局变量 n(共享数据)被多线程访问。当 TA 获得锁后,在临界区中访问 n,且只有当 n > 0 时,才会释放锁。这意味着当 n == 0 时,TA 将永远不会释放锁,从而造成死锁。

那么解决死锁的方法,就是设定一个条件:只有当 n > 时,TA 才可以获得锁。而这个条件,就是多线程之间需要同步的信息。即:在多线程环境中,当一个线程需要等待某个条件成立时,才可以获得锁,那么应该使用条件变量来实现。

  • 同步:pthread 条件变量提供了一种线程同步机制,当特定的事件发生时,它可以唤醒一个或多个在等待事件的线程,从而实现线程间的同步和协调。条件变量通常与互斥锁一起使用,以避免竞态条件和死锁的发生。

pthread_cond_init()

函数作用:用于初始化一个条件变量实体。

函数原型

  • cond 参数:pthread_cond_t 类型指针,用于指定要初始化的条件变量。
  • attr 参数:pthread_condattr_t 类型指针,用于指定条件变量的属性,通常为 NULL。
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

pthread_cond_wait()

函数作用:线程用于等待某个条件变量满足。

  1. 当 T1 线程调用 pthread_cond_wait() 时,会自动地释放掉互斥锁,并阻塞线程,开始等待。
  2. 直到另一个 T2 线程调用了 pthread_cond_signal() 或 pthread_cond_broadcast(),以此来通知 T1 条件变量满足了。
  3. 然后 T1 pthread_cond_wait() 重新获取指定的互斥锁并返回。

函数原型

  • cond 参数:pthread_cond_t 类型指针,用于指定要等待的条件变量。
  • mutex 参数:pthread_mutex_t 类型指针,用于指定要关联的互斥锁。在等待期间,线程将释放该互斥锁。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

pthread_cond_signal()

函数作用:用于向等待条件变量的线程发送信号,唤醒其中的一个线程。

函数原型

  • cond 参数:pthread_cond_t 类型指针,用于指定要发送信号的条件变量。
int pthread_cond_signal(pthread_cond_t *cond);

pthread_cond_broadcast()

函数作用:用于向等待条件变量的所有线程发送信号,唤醒所有等待的线程。

函数原型

  • cond 参数:pthread_cond_t 类型指针,用于指定要发送信号的条件变量。
int pthread_cond_broadcast(pthread_cond_t *cond);

互斥锁和条件变量配合使用

当一个线程需要某个条件成立后才可以访问共享数据时。需要先锁定一个互斥锁,然后检查条件变量,如果条件不满足,则需要挂起并等待。

#include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;int data = 0;  // 共享数据void *producer(void *arg)
{for (int i = 0; i < 10; i++) {pthread_mutex_lock(&mutex); // 加锁data++; // 修改共享数据pthread_cond_signal(&cond); // 发送信号pthread_mutex_unlock(&mutex); // 解锁sleep(1);}pthread_exit(NULL);
}void *consumer(void *arg)
{while (1) {pthread_mutex_lock(&mutex); // 加锁while (data == 0) { // 如果没有数据就等待信号pthread_cond_wait(&cond, &mutex);}printf("data = %d\n", data); // 打印共享数据data--; // 修改共享数据pthread_mutex_unlock(&mutex); // 解锁sleep(1);}pthread_exit(NULL);
}int main()
{pthread_t tid1, tid2;pthread_create(&tid1, NULL, producer, NULL);pthread_create(&tid2, NULL, consumer, NULL);pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}

线程非安全标准库函数

C 语言提供的大部分标准库函数都是线程安全的,但是也有几个常用函数是线程不安全的,也称为不可重入函数,原因是使用了某些全局或者静态变量。

我们知道,全局变量和静态变量分别对应内存中的全局变量区和静态存储区,这些区域都是可以跨线程访问的。在多线程环境中,这些数据如果在没有加锁的情况下并行读写,就会造成 Segmentfault / CoreDump 之类的问题。

  • 不可重入函数汇总:

C 语言编程 — pthread 用户线程操作相关推荐

  1. 线程打印_Java编程核心技术之——线程操作

    Java中常见的线程操作包括线程休眠.线程等待(插入).线程中断,具体案例如下: ThreadOptCases.java: import java.util.Vector; import java.u ...

  2. linux怎么杀死线程c语言,教程-linux下c语言编程 第一弹-线程的使用

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 /*linux下最常用的应该就是pthread线程库了 本教程就是关于pthread的 关于线程是什么东西我就不赘述了 百科上都有*/ #include ...

  3. C语言编程网站用户管理系统,C语言系统用户管理系统Word版

    <C语言系统用户管理系统Word版>由会员分享,可在线阅读,更多相关<C语言系统用户管理系统Word版(13页珍藏版)>请在人人文库网上搜索. 1.传播优秀Word版文档 ,希 ...

  4. c语言如何把变量按位颠倒,求答案,用C语言编程,用户输入一个正整数,把他的各位数字前后颠倒,并输入点到后的结果...

    满意答案 q15173278975 推荐于 2017.11.26 采纳率:58%    等级:13 已帮助:5665人 用字符串处理很简单 #include #include void main () ...

  5. Linux学习之C语言的进程与线程编程

    前言 继续Linux的学习,学到了Linux系统下的进程与线程的概念,布置了有关其的实验题,用C语言编程启动进程线程,习惯了Java多线程编程,这次在Linux下玩一玩C语言进程线程编程. 本文原创, ...

  6. c语言编程 目录,C语言编程实例简介,目录书摘

    内容简介: <C语言编程实例>将蓝牙小车作为教学组织引领,开展各章节.模块的教学,在教学的过程中把C语言编程.VC++软件操作.单片机外围电路搭建与调试.CAD机械制图.激光切割零件制作等 ...

  7. Linux C :线程操作和线程同步的多线程并发编程

    在这之前可以先看看这边文章了解线程概念,信号量,条件变量,死锁.管程等概念 https://blog.csdn.net/superSmart_Dong/article/details/11666837 ...

  8. Linux C编程--线程操作1--线程概述和简单的线程操作

    关于linux线程 在许多经典的操作系统教科书中, 总是把进程定义为程序的执行实例, 它并不执行什么, 只是维护应用程序所需的各种资源. 而线程则是真正的执行实体.  为了让进程完成一定的工作, 进程 ...

  9. java切入式编程显示屏_C语言嵌入式系统编程修炼之四:屏幕操作

    C语言嵌入式系统编程修炼之四:屏幕操作 作者:宋宝华   更新日期:2005-07-22 汉字处理 现在要解决的问题是,嵌入式系统中经常要使用的并非是完整的汉字库,往往只是需要提供数量有限的汉字供必要 ...

最新文章

  1. 希尔排序——算法系列
  2. 【Linux】一步一步学Linux——top命令(121)
  3. Mybatis高级应用 整合Ehcache
  4. DFS 事件id 2104 2004 报错
  5. mysql员工与部门代码,springMVC入门实例(员工系统-Mysql)代码简单易懂
  6. 单图像超分辨率重建总结
  7. 【每天学一点新知识】getshell???webshell???
  8. 算法题目打卡:Ques20201025
  9. yaourt下载速度太慢_加快Yaourt软件包的安装速度
  10. laravel 微信授权登录
  11. html 页面新窗口打开,HTML 在新窗口打开全站链接
  12. [易飞]凭证尾行空白显示异常处理
  13. netty-读半包处理--ByteToMessageDecoder
  14. linux mysql backup(数据库备份)
  15. 自建外贸独立站需要多少钱。
  16. matlab在高等数学中的,matlab在高等数学中的应用
  17. aiff 文件格式简述
  18. 什么是死锁及死锁的必要条件和解决方法
  19. zznu 我只看看不写题(贪心+优先队列)
  20. C语言跳过障碍物小游戏,html5飞翔的小鸟越过障碍物小游戏代码

热门文章

  1. Vue项目后台部分4,分类管理以及详情管理,添加和修改,loading效果,窗体弹出
  2. ober试验视频_手法医生各种试验
  3. DMA是什么?怎么出现的?
  4. canvas画图(web作业)
  5. unity 物理台球_使用基于Android物理的动画制作类似于桌球的游戏
  6. CodeForces---787D:Legacy【线段树优化建图+最短路】
  7. 苍龙新玩法-主角自己全自动指令通关攻略
  8. Google如何识别重复内容的主要版本
  9. 2021年B证(安全员)考试试卷及B证(安全员)考试技巧
  10. 甘肃安全员B证怎么考单选题库