进程(Process):
线程(Thread):
当你运行一个程序时,操作系统会为该程序创建一个进程(Process)。这个进程可以包含一个或多个线程(Thread)。主线程就是其中之一,它执行main
函数中的代码。
所以,当main
函数运行时,你可以说它运行在应用程序的主线程中,而应用程序本身运行在一个进程中。这个进程可能会包含其他辅助线程,这些线程可以执行各种任务,但主线程通常是应用程序的控制中心,用于协调和管理其他线程的执行。
总的来说,虽然在特定情况下可以创建多个进程来运行不同的main
程序,但通常情况下,一个主程序对应一个进程。如果需要实现多个并发任务,更常见的做法是使用多线程技术,将不同的任务分配给不同的线程在同一个进程中执行。
ret = pthread_attr_init(&attr);
if (ret)
{
printf("init pthread attributes failed\n");
goto out;
}
/* Set a specific stack size */
ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + MY_STACK_SIZE);
if (ret)
{
printf("pthread setstacksize failed\n");
goto out;
}
/* Set scheduler policy and priority of pthread */
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret)
{
printf("pthread setschedpolicy failed\n");
goto out;
}
param.sched_priority = 80;
ret = pthread_attr_setschedparam(&attr, ¶m);
if (ret)
{
printf("pthread setschedparam failed\n");
goto out;
}
/* Use scheduling parameters of attr */
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret)
{
printf("pthread setinheritsched failed\n");
goto out;
}
/* Create a pthread with specified attributes */
ret = pthread_create(&thread, &attr, simple_cyclic_task, NULL);
pthread_attr_init(&attr)
:初始化线程属性对象 attr
。如果初始化失败,将打印错误消息并跳转到 out
标签。
pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + MY_STACK_SIZE)
:设置线程的堆栈大小。这里使用了宏 PTHREAD_STACK_MIN
表示系统定义的最小堆栈大小,并加上 MY_STACK_SIZE
作为自定义的堆栈大小。如果设置失败,将打印错误消息并跳转到 out
标签。
pthread_attr_setschedpolicy(&attr, SCHED_FIFO)
:设置线程的调度策略为 SCHED_FIFO
(先进先出)。这是一种实时调度策略,用于优先级较高的线程。如果设置失败,将打印错误消息并跳转到 out
标签。
pthread_attr_setschedparam(&attr, ¶m)
:设置线程的调度参数,其中 param
是一个包含了线程调度参数的结构体。如果设置失败,将打印错误消息并跳转到 out
标签。
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)
:设置线程继承调度参数的方式为 PTHREAD_EXPLICIT_SCHED
,这意味着线程将使用显式设置的调度策略和参数。如果设置失败,将打印错误消息并跳转到 out
标签。
pthread_create(&thread, &attr, simple_cyclic_task, NULL)
:创建一个线程,线程的属性由 attr
指定,线程的入口点是 simple_cyclic_task
函数。如果创建失败,将打印错误消息并跳转到 out
标签。
pthread_setname_np(thread, "ethercat-rt")
:设置线程的名称为 "ethercat-rt"。这是一个非标准的 POSIX 扩展,用于设置线程的名称。
pthread_join(thread, NULL)
:等待线程 thread
完成执行。如果等待失败,将打印错误消息。
最后,程序通过 return ret;
返回 ret
变量,其中 ret
包含了最后一个操作的返回值(通常是 pthread_join
的返回值)。
这段代码的主要作用是创建一个线程并配置线程的属性,然后等待该线程完成执行。如果在配置或创建线程的过程中出现错误,程序将在错误消息中报告问题,并跳转到 out
标签,最终返回错误代码。
创建线程的主要目的是允许程序在多个执行路径上并发执行任务,从而提高程序的性能和响应性。线程是进程内的轻量级执行单元,它可以独立执行代码,具有自己的堆栈和寄存器状态,但与同一进程内的其他线程共享进程的内存和资源。线程之间的通信和数据共享通常更加高效,因为它们可以直接访问同一进程的内存空间,而无需像不同进程之间那样使用复杂的进程间通信机制。
一些程序不需要显式创建线程,因为它们是单线程程序,或者它们使用操作系统的默认主线程来执行任务。在这种情况下,程序的所有操作都在一个线程中进行,没有并发执行。然而,对于需要同时处理多个任务或需要更好地利用多核处理器的应用程序,创建额外的线程可以帮助提高性能。
以下是一些创建线程的常见用例:
并行处理:某些任务可以被拆分成多个子任务,并且这些子任务可以并发执行以加快处理速度。例如,一个图像处理应用程序可以创建多个线程来同时处理多张图像。
响应性:多线程可以用于提高程序的响应性。在GUI应用程序中,一个线程可以用于处理用户界面事件,而另一个线程可以用于执行后台任务,以确保用户界面保持响应。
并发服务器:服务器应用程序通常需要同时处理多个客户端请求。每个客户端请求可以在单独的线程中处理,从而提供并发性。
计算密集型任务:对于需要大量计算的任务,可以创建多个线程以充分利用多核处理器的计算能力。
虽然创建线程可以提高程序的性能和并发性,但也需要谨慎使用,因为线程之间的并发操作可能导致复杂的同步和竞态条件问题。因此,在编写多线程程序时,需要仔细考虑线程之间的协作和数据共享,以确保程序的正确性和稳定性。有些程序可能因为任务简单或者不需要并发处理而没有显式创建线程。不同的应用程序具有不同的需求,是否需要创建线程取决于具体的应用场景和目标。
在大多数情况下,一旦创建了线程并调用了 pthread_create
函数,新创建的线程将立即开始执行线程入口函数,而不会等待主线程或其他线程继续执行。这意味着线程入口函数会在一个新线程上并行执行。
因此,在常规情况下,以下代码中的设置线程名称和其他操作将在新线程开始执行之前执行:
ret = pthread_create(&thread, &attr, simple_cyclic_task, NULL);
if (ret) { printf("create pthread failed: %s\n", strerror(ret)); goto out; }
ret = pthread_setname_np(thread, "ethercat-rt");
if (ret) { printf("failed to set thread name\n"); }
上述代码中,pthread_create
函数创建了一个新线程并立即开始执行 simple_cyclic_task
函数。然后,代码立即尝试设置线程名称。由于这两个操作是并行执行的,因此在设置线程名称时,新线程可能已经在执行或者正在执行 simple_cyclic_task
函数。
如果确实需要等待新线程的执行完成,以确保在执行其他操作之前线程已经初始化完毕,可以使用 pthread_create
返回的线程句柄 thread
来调用 pthread_join
函数,这将会等待新线程的结束。例如:
ret = pthread_create(&thread, &attr, simple_cyclic_task, NULL);
if (ret)
{ printf("create pthread failed: %s\n", strerror(ret));
goto out; } // 等待新线程执行完毕
ret = pthread_join(thread, NULL);
if (ret) { printf("join pthread failed: %s\n", strerror(ret)); }
上述代码会等待新线程执行完毕,然后再继续执行设置线程名称等其他操作。这样可以确保在新线程完全初始化之前不会执行后续操作。