嵌入式Linux:线程的创建、终止、回收、取消和分离

线程的创建、终止、取消、回收和分离操作是多线程编程的核心。

嵌入式Linux:线程的创建、终止、回收、取消和分离

在多线程编程中,需要妥善管理线程的生命周期,以避免资源泄漏、竞争条件或僵尸线程等问题。

1、创建线程

在 Linux 中,默认情况下,一个进程启动时是单线程运行的,这个线程被称为 主线程。

然而,现代计算任务通常需要并行处理,主线程可以通过 pthread_create() 函数创建额外的线程来并行执行任务。

这些额外的线程与主线程共享进程的资源(如内存空间、文件描述符等),但它们有独立的执行路径。

pthread_create() 函数的定义如下:

代码语言:javascript代码运行次数:0运行复制

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,                   void *(*start_routine) (void *), void *arg);

函数参数:

thread:这是一个 pthread_t 类型的指针,指向存储线程 ID 的变量。pthread_t 是用于唯一标识线程的类型,当创建线程成功时,该变量会被赋值为新线程的 ID,在后续的线程管理中使用。attr:这是一个指向 pthread_attr_t 类型的指针,用于设置线程的属性。如果设置为 NULL,则使用线程的默认属性。这些属性包括线程是否分离(detached)、栈大小等。如果需要设置特定的属性,可以使用 pthread_attr_init() 和相关的属性函数。start_routine:这是线程执行函数的指针。新线程创建后,会从这个函数开始执行。该函数必须符合以下原型:代码语言:javascript代码运行次数:0运行复制

void* (*start_routine)(void *arg);

它接收一个 void* 类型的参数(arg),并返回一个 void* 类型的返回值。

arg:这是传递给 start_routine 函数的参数。可以是任意类型的指针。如果不需要传递参数,可以设置为 NULL。如果需要传递多个参数,可以使用结构体将它们打包后通过该指针传入。

返回值:

成功时返回 0。失败时返回错误号,表示失败的原因。例如,EAGAIN 表示系统资源不足无法创建新线程,EINVAL 表示传入的属性无效。

创建线程的关键点:

线程 ID: 每个线程都有唯一的 ID,用于区分线程。创建线程时,pthread_create() 会将新线程的 ID 存储在 pthread_t 类型的变量中,便于后续操作。线程属性: 默认情况下,线程使用系统的默认属性。如果需要更改线程的属性,比如将其设置为 分离线程 或指定线程的栈大小,可以通过 pthread_attr_t 来设置。启动函数和参数: 新线程会从 start_routine 函数开始执行,并传入 arg 参数。可以通过将多个参数封装在结构体中,一并传递给该函数。

当一个新线程被创建后,它立即加入系统的 线程调度队列,并在合适的时机获取 CPU 执行时间。

由于调度是由操作系统控制的,所以无法预料新创建的线程和主线程谁会先执行。

如果程序对线程的执行顺序有严格要求,可以使用同步机制(如 互斥锁 或 信号量)来控制线程间的执行顺序。

下面是一个创建线程并传递参数的简单示例:

代码语言:javascript代码运行次数:0运行复制

void* thread_function(void* arg) {    int* num = (int*)arg;    printf("New thread running with argument: %dn", *num);    return NULL;}int main() {    pthread_t thread;    int arg = 42;    // 创建线程,传递参数 arg    if (pthread_create(&thread, NULL, thread_function, &arg) != 0) {        perror("pthread_create failed");        return 1;    }    // 等待新线程执行完毕    pthread_join(thread, NULL);    printf("Main thread finishedn");    return 0;}

解释:

线程函数 thread_function() 接收一个 void* 类型的参数,并将其强制转换为 int* 类型,打印传入的值。主线程 调用 pthread_create() 创建了一个新线程,并将 arg 作为参数传递给新线程的函数 thread_function()。创建线程后,主线程调用 pthread_join() 等待新线程完成执行。如果不使用 pthread_join(),主线程不会等待新线程结束,这可能导致程序提前退出。

2、终止线程

在 Linux 中,终止线程可以通过多种方式完成,不同的方式影响线程的退出行为和进程的状态管理。

我们详细说明几种终止线程的常用方法。

return: 当线程的 start 函数执行 return 时,线程正常终止,并返回指定的值,返回值可以通过 pthread_join() 获取。pthread_exit(): 线程可以通过显式调用 pthread_exit() 来终止自身,pthread_exit() 允许线程在任何位置退出,返回的值也可以通过 pthread_join() 获取。pthread_cancel(): 通过 pthread_cancel() 可以请求取消一个线程,线程需要响应取消请求才能终止。exit() 和 _exit(): 当进程中的任意线程调用 exit()、_exit() 或 _Exit() 时,整个进程,包括所有线程,都会被终止。2.1、通过 return 语句退出线程

线程的 start 函数(即传递给 pthread_create() 的函数)在执行完毕时,可以直接使用 return 语句返回。这种方式会使线程正常退出,并将返回值作为线程的退出码。

这与调用 pthread_exit() 类似。

代码语言:javascript代码运行次数:0运行复制

void* thread_function(void* arg) {    // 执行一些任务    int result = 42;    return (void*)result; // 通过 return 语句退出线程}

在上面的代码中,线程执行完 thread_function() 后,通过 return 返回 result,并且这个返回值可以通过 pthread_join() 函数在主线程中获取。

2.2、通过 pthread_exit() 退出线程

pthread_exit() 是专门用于退出线程的函数,它允许线程在任何位置显式退出,而不是依赖于 return。

调用 pthread_exit() 后,线程的控制流会立即结束,不再执行后续代码。

pthread_exit() 函数原型:

代码语言:javascript代码运行次数:0运行复制

void pthread_exit(void *retval);

参数 retval: retval 是一个 void* 类型的指针,指定线程的返回值,也就是线程的退出码。这个值可以被其他线程通过 pthread_join() 获取。

示例如下:

代码语言:javascript代码运行次数:0运行复制

void* thread_function(void* arg) {    int* retval = (int*)arg;    printf("Thread exiting with value: %dn", *retval);    pthread_exit(retval); // 显式退出线程并返回值}int main() {    pthread_t thread;    int arg = 42;    int* retval;    pthread_create(&thread, NULL, thread_function, &arg);    pthread_join(thread, (void**)&retval); // 获取线程的退出码    printf("Thread returned: %dn", *retval);    return 0;}

解释:

在该示例中,pthread_exit() 显式终止了线程,并返回参数 arg 的值。主线程通过 pthread_join() 获取了子线程的退出码。2.3、通过 exit()、_exit() 或 _Exit() 终止整个进程

exit()、_exit() 和 _Exit() 不是用于终止单个线程的,而是用于终止整个进程。

由于线程共享同一个进程资源,如果任意一个线程调用这些函数,整个进程(包括所有线程)都会终止。

exit(): 正常终止进程,执行清理函数、关闭文件描述符等。_exit() 和 _Exit(): 立即终止进程,不执行清理工作。

以下示例中,thread_function() 中调用了 exit(),导致整个进程被终止,主线程也不会继续执行。

代码语言:javascript代码运行次数:0运行复制

void* thread_function(void* arg) {    printf("Thread running...n");    exit(0); // 调用 exit(),导致整个进程终止    return NULL;}int main() {    pthread_t thread;    pthread_create(&thread, NULL, thread_function, NULL);    pthread_join(thread, NULL);    // 如果没有被 exit() 终止,主线程会继续执行这行代码    printf("Main thread finishedn");    return 0;}

3、回收线程

在多线程编程中,当线程结束后,其占用的资源不会立即被系统释放,除非显式回收这些资源,否则这些线程会变成 僵尸线程。

在 Linux 中,回收线程的操作与进程的回收类似。

正如进程中的父进程可以使用 wait() 来回收子进程的资源,线程中也需要通过 pthread_join() 来回收线程资源并获取线程的退出状态。

pthread_join() 是用于 等待指定线程终止并回收其资源 的函数,它会阻塞调用线程直到目标线程终止。

如果线程已经终止,pthread_join() 将立即返回。

通过这个函数,主线程或其他线程可以获取目标线程的退出状态,并清理其占用的资源,避免产生僵尸线程。

pthread_join() 的函数原型:

代码语言:javascript代码运行次数:0运行复制

int pthread_join(pthread_t thread, void **retval);

函数参数说明:

thread: 这是目标线程的线程 ID,pthread_join() 将等待这个线程终止。retval: 这是一个指向 void* 类型的指针,指向保存线程返回值的内存地址。如果目标线程通过 pthread_exit() 或 return 语句返回了某个值,这个值将被存储在 *retval 指向的内存中。如果 retval 为 NULL,则表示不关心目标线程的返回值。

返回值:

成功时返回 0。如果调用失败,pthread_join() 将返回一个错误码。例如,ESRCH 表示指定的线程不存在,EINVAL 表示线程不可被 pthread_join() 回收,或者调用线程尝试等待自身终止。

以下例子中,线程执行完 thread_function() 后通过 pthread_exit() 返回 result。

主线程调用 pthread_join() 等待线程结束,并成功获取到了线程的返回值。

代码语言:javascript代码运行次数:0运行复制

void* thread_function(void* arg) {    int result = 100;    printf("Thread running...n");    pthread_exit((void*)&result); // 显式返回一个结果}int main() {    pthread_t thread;    int* thread_result;    // 创建线程    pthread_create(&thread, NULL, thread_function, NULL);    // 回收线程并获取返回值    pthread_join(thread, (void**)&thread_result);    printf("Thread returned: %dn", *thread_result);    return 0;}

3.1、pthread_join() 的使用场景与注意事项

pthread_join() 是 阻塞函数,它会一直等待指定线程结束。如果目标线程需要执行大量计算或处理,调用 pthread_join() 的线程将一直处于等待状态,直到目标线程终止。

如果线程已经结束,pthread_join() 将立即返回。

以下示例中,主线程在调用 pthread_join() 时会等待 5 秒,直到 worker_function() 执行完毕为止。

代码语言:javascript代码运行次数:0运行复制

void* worker_function(void* arg) {    sleep(5);  // 模拟一些长时间运行的操作    return NULL;}int main() {    pthread_t thread;    pthread_create(&thread, NULL, worker_function, NULL);    printf("Waiting for thread to finish...n");    pthread_join(thread, NULL); // 阻塞等待线程结束    printf("Thread finished.n");    return 0;}

在进程中,如果父进程不回收子进程,则子进程会变为 僵尸进程,占用系统资源。

同样的,如果一个线程终止后,没有被其他线程调用 pthread_join() 来回收,其内存和其他资源也不会被立即释放,这就导致了 僵尸线程 的问题。

僵尸线程不仅浪费资源,而且如果僵尸线程累积过多,可能会导致应用程序无法创建新的线程。

3.2、pthread_join() 与进程回收的区别

虽然 pthread_join() 与进程中的 waitpid() 类似,都是用于等待子线程(或子进程)结束并获取其退出状态,但二者之间有一些显著的区别:

1、线程关系是对等的。

在多线程程序中,任何线程都可以调用 pthread_join() 来等待另一个线程的结束。即使是非创建该线程的线程,也可以调用 pthread_join() 来等待它的终止。线程之间没有父子层级关系。

举例来说,如果线程 A 创建了线程 B,线程 B 创建了线程 C,那么线程 A 可以等待线程 C 的结束,而不需要依赖线程 B。

这与进程的父子层级结构不同,父进程是唯一可以调用 wait() 或 waitpid() 来等待子进程终止的进程。

2、pthread_join() 不支持非阻塞等待。

pthread_join() 是阻塞调用,不支持类似waitpid() 中的非阻塞模式(通过传入 WNOHANG 标志实现)。

AIBox 一站式AI创作平台 AIBox 一站式AI创作平台

AIBox365一站式AI创作平台,支持ChatGPT、GPT4、Claue3、Gemini、Midjourney等国内外大模型

AIBox 一站式AI创作平台 31 查看详情 AIBox 一站式AI创作平台

这意味着线程调用 pthread_join() 后必须等待目标线程终止,不能做其他操作。如果需要更复杂的线程同步,通常需要引入其他机制,如 信号量、条件变量 等。

4、取消线程

通常情况下,线程会自行决定何时结束,比如通过调用 pthread_exit() 函数或者在其启动函数中执行 return 语句。

但有些场景下,主线程或其他线程可能需要 强制终止 某个正在运行的线程,这时就可以通过 取消请求 来实现。

通过调用 pthread_cancel(),可以向目标线程发送一个取消请求,要求它终止。

pthread_cancel() 函数原型如下:

代码语言:javascript代码运行次数:0运行复制

int pthread_cancel(pthread_t thread);

参数说明:

thread: 需要取消的目标线程的线程 ID。

返回值:

成功时返回 0。如果调用失败,返回错误码,例如:ESRCH: 指定的线程不存在。4.1、线程取消的响应机制

目标线程对取消请求的响应方式可以由其自身决定。每个线程都有一个 取消状态 和 取消类型 来控制它对取消请求的响应:

4.1.1、取消状态

取消状态决定了线程是否允许响应取消请求,线程可以通过调用 pthread_setcancelstate() 来修改其取消状态。

PTHREAD_CANCEL_ENABLE: 表示线程 允许 响应取消请求(这是默认状态)。PTHREAD_CANCEL_DISABLE: 表示线程 不允许 响应取消请求。即使收到了取消请求,线程仍会继续运行,直到其取消状态被重新设置为可取消。

pthread_setcancelstate() 函数原型:

代码语言:javascript代码运行次数:0运行复制

int pthread_setcancelstate(int state, int *oldstate);

参数:

state: 可以是 PTHREAD_CANCEL_ENABLE 或 PTHREAD_CANCEL_DISABLE,分别表示开启或禁用取消请求的响应。oldstate: 如果不为 NULL,将保存原先的取消状态。

示例如下:

代码语言:javascript代码运行次数:0运行复制

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); // 禁止取消请求pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);  // 允许取消请求

4.1.2、取消类型

取消类型决定了线程 何时 响应取消请求。

可以通过调用 pthread_setcanceltype() 来设置线程的取消类型:

PTHREAD_CANCEL_DEFERRED: 线程将在 某些特定的取消点 响应取消请求(例如调用 pthread_testcancel(),或进行 I/O 操作时)。这是默认的取消类型。PTHREAD_CANCEL_ASYNCHRONOUS: 线程在 收到取消请求的瞬间 就立即响应,可能导致线程在任意位置被取消。

pthread_setcanceltype() 函数原型:

代码语言:javascript代码运行次数:0运行复制

int pthread_setcanceltype(int type, int *oldtype);

参数:

type: 可以是 PTHREAD_CANCEL_DEFERRED 或 PTHREAD_CANCEL_ASYNCHRONOUS,分别表示延迟响应取消或立即响应取消。oldtype: 如果不为 NULL,将保存原先的取消类型。

示例:

代码语言:javascript代码运行次数:0运行复制

pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); // 设置为立即响应取消pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);      // 设置为延迟响应取消

4.2、取消点与线程清理

当线程的取消类型设置为 PTHREAD_CANCEL_DEFERRED 时,线程只有在到达某些 取消点 时才会响应取消请求。

取消点通常是一些耗时操作或系统调用,比如:

pthread_testcancel(): 显式设置取消点。其他一些常见的系统调用,比如 I/O 操作、sleep()、select() 等,都是隐式取消点。

嵌入式Linux:线程的创建、终止、回收、取消和分离

系统中还有许多函数可以作为取消点,这里不再逐一列举。您可以通过查看 man 手册获取更多信息,使用命令 man 7 pthreads 进行查询。

pthread_testcancel() 函数原型:

代码语言:javascript代码运行次数:0运行复制

void pthread_testcancel(void);

这个函数可以在代码的任意位置显式创建一个取消点。

调用 pthread_testcancel() 后,线程会检查是否有取消请求,如果有,线程将在此处退出。

示例如下:

代码语言:javascript代码运行次数:0运行复制

while (1) {    // 执行一些任务    pthread_testcancel(); // 在循环中显式设置取消点,检查是否有取消请求}

4.3、线程清理处理函数

在线程终止时(无论是正常结束还是被取消),可以使用 清理处理函数 来进行资源清理。

清理处理函数可以确保线程在取消时能够正确释放资源,避免资源泄露。

使用 pthread_cleanup_push() 和 pthread_cleanup_pop() 来设置清理函数:

pthread_cleanup_push(void (*routine)(void *), void *arg):将一个清理函数 routine 压入栈,当线程退出时,系统将调用该函数。pthread_cleanup_pop(int execute):将清理函数从栈中弹出,execute 表示是否执行该函数。

以下例子中,当线程收到取消请求后,它会在 pthread_testcancel() 函数处响应取消请求并退出。

在线程退出时,cleanup_handler() 会被调用以清理资源。

代码语言:javascript代码运行次数:0运行复制

void cleanup_handler(void *arg) {    printf("Cleanup: %sn", (char *)arg);}void* thread_function(void* arg) {    pthread_cleanup_push(cleanup_handler, "Thread resources"); // 设置清理函数    while (1) {        printf("Thread running...n");        sleep(1);        pthread_testcancel(); // 检查是否有取消请求    }    pthread_cleanup_pop(1); // 1 表示执行清理函数    return NULL;}int main() {    pthread_t thread;    pthread_create(&thread, NULL, thread_function, NULL);    sleep(3);  // 等待几秒钟    pthread_cancel(thread); // 发送取消请求    pthread_join(thread, NULL); // 等待线程结束    printf("Thread has been canceled.n");    return 0;}

正确处理线程的取消操作对于复杂的多线程应用程序至关重要,特别是在执行长时间任务时,灵活管理线程的取消状态和清理行为能够有效提高系统的稳定性和可靠性。

pthread_cancel() 用于向目标线程发送取消请求,要求其终止,但目标线程是否终止取决于其取消状态和取消类型。线程可以通过 pthread_setcancelstate() 来控制是否响应取消请求,并通过 pthread_setcanceltype() 来控制何时响应。在使用 延迟取消 的情况下,线程只有在特定的 取消点 处才会检查取消请求,可以通过 pthread_testcancel() 显式设置取消点。清理处理函数 确保线程在被取消时能够正确释放资源,避免资源泄露。

5、分离线程

默认情况下,线程终止后,其资源不会立即被系统回收,除非有另一个线程通过 pthread_join() 函数显式地等待该线程终止,回收其资源。

但如果某些线程的退出状态和返回值对程序来说并不重要,且不希望手动调用 pthread_join(),可以将该线程设置为 分离状态。

分离状态的线程在终止时,系统会自动回收它的资源。

要将线程设置为分离状态,可以调用 pthread_detach() 函数。

pthread_detach() 函数原型:

代码语言:javascript代码运行次数:0运行复制

int pthread_detach(pthread_t thread);

参数说明:

thread: 需要分离的目标线程的线程 ID。

返回值:

成功时返回 0。如果调用失败,返回错误码,例如:ESRCH: 指定的线程不存在或已经被回收。EINVAL: 线程已经处于分离状态。

调用 pthread_detach() 后,指定的线程会进入分离状态。

处于分离状态的线程在终止时,系统会自动回收其所有资源,而无需其他线程显式调用 pthread_join()。

分离状态是不可逆的,一旦线程被分离,就不能再通过 pthread_join() 获取该线程的返回状态或等待其结束。

以下例子中,创建了一个新线程,并通过 pthread_detach() 将其分离。之后,无需调用 pthread_join(),系统将在该线程终止时自动回收它的资源。

代码语言:javascript代码运行次数:0运行复制

pthread_t thread;pthread_create(&thread, NULL, thread_function, NULL);pthread_detach(thread); // 将该线程设置为分离状态

线程不仅可以由其他线程分离,还可以通过调用 pthread_detach(pthread_self()) 来 分离自己。

这意味着该线程在终止时不需要其他线程来回收资源,系统将自动处理。

示例如下:

代码语言:javascript代码运行次数:0运行复制

void* thread_function(void* arg) {    pthread_detach(pthread_self()); // 分离自己    // 线程执行的其他操作    pthread_exit(NULL);}

线程分离机制特别适用于以下几种场景:

不关心线程的返回值: 如果线程执行的任务不需要返回值,且不希望其他线程显式地等待它结束。避免僵尸线程: 僵尸线程是指已经终止但资源未被回收的线程,长时间存在僵尸线程会消耗系统资源。将线程设置为分离状态,可以避免僵尸线程的产生。长时间运行的后台任务: 如果线程运行时间较长或是后台任务,而主线程不需要等待其结束,分离该线程可以简化资源管理。

线程分离与 pthread_join() 的比较:

线程分离:

使用 pthread_detach() 将线程设置为分离状态。系统在线程终止时自动回收资源。无法通过 pthread_join() 获取线程的返回值或等待线程终止。

pthread_join():

主动调用 pthread_join() 等待指定线程终止并回收资源。可以获取线程的返回值或终止状态。若没有调用 pthread_join(),线程终止后会成为僵尸线程,直到其资源被回收。

线程分离在简化多线程程序的资源管理方面非常有用,特别是对于一些无需等待或回收的线程,可以通过分离机制优化程序的性能和稳定性。

最后讲两点注意事项:

不可逆性: 一旦线程被设置为分离状态,就无法恢复到可被 pthread_join() 回收的状态。如果你将某个线程分离,后续便无法获取其返回值或等待它结束。线程同步问题: 如果某个线程执行的任务需要与其他线程同步完成,则不应将其分离。否则,主线程或其他线程可能无法等待该线程结束,导致任务未完成就继续执行。

以上就是嵌入式Linux:线程的创建、终止、回收、取消和分离的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/485636.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
智能助手系统中的个性化推荐问题
上一篇 2025年11月8日 13:16:12
vscode如何执行gml vscode搭建gamemaker环境
下一篇 2025年11月8日 13:16:29

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    900
  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    100
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    000
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    100
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    000
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    000
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    200
  • php常量怎么用_PHP常量(define/const)定义与使用方法

    PHP中可通过define函数和const关键字定义常量,用于存储不可变值。define适用于全局作用域,支持动态名称和条件定义,如define(‘SITE_NAME’, ‘MyWebsite’);const在编译时生效,语法简洁但限制多,只能在类或全…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    000
  • 创建指定大小并填充特定数据的Golang文件教程

    本文将介绍如何使用Golang创建一个指定大小的文件,并用特定数据填充它。我们将使用 `os` 包提供的函数来创建和截断文件,从而实现快速生成大文件的目的。示例代码展示了如何创建一个10MB的文件,并将其填充为全零数据。掌握这些方法,可以方便地在例如日志系统或磁盘队列等场景中,预先创建测试文件或初始…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    000
  • Discord.py 交互按钮超时与持久化解决方案

    本教程旨在解决Discord.py中交互按钮在一段时间后出现“This Interaction Failed”错误的问题。我们将深入探讨视图(View)的超时机制,并提供通过正确设置timeout参数以及利用bot.add_view()方法实现按钮持久化的具体方案,确保您的机器人交互功能稳定可靠,即…

    2026年5月10日
    000
  • Debian Copilot的社区活跃度如何

    debian copilot是codeberg社区维护的ai助手,旨在为debian用户提供服务。尽管搜索结果中没有直接提供关于debian copilot社区支持活跃度的具体数据,但我们可以通过debian社区的整体活跃度和特点来推断其活跃性。 Debian社区的一般情况: Debian拥有详尽的…

    2026年5月10日
    000
  • JavaScript 动态菜单点击高亮效果实现教程

    本教程详细介绍了如何使用 JavaScript 实现动态菜单的点击高亮功能。通过事件委托和状态管理,当用户点击菜单项时,被点击项会高亮显示(绿色),同时其他菜单项恢复默认样式(白色)。这种方法避免了不必要的DOM操作,提高了性能和代码可维护性,确保了无论点击方向如何,功能都能稳定运行。 动态菜单高亮…

    2026年5月10日
    200

发表回复

登录后才能评论
关注微信