【p2p、分布式,区块链笔记 UPNP】: Libupnp test_init.c 03 初始化SDK --- 线程池初始化(UpnpInitThreadPools)

  • 线程池初始化相关代码声明在https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/threadutil/ThreadPool.h。线程池配置相关代码位置:https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/inc/config.h#L76-L105

相关数据结构:

  • 以下是代码中几个主要结构体的作用说明,它们涉及线程池的配置、任务和统计等功能:
    • ThreadPoolAttr 用于配置线程池的属性,如线程的最小、最大数量,空闲时间等。
    • ThreadPoolJob 定义了线程池中的任务,包括任务的执行函数、参数和优先级。
    • ThreadPoolStats 用于记录线程池的统计信息,如任务执行时间、任务队列情况等。
    • ThreadPool 是线程池的核心结构,管理线程、任务队列以及线程池的运行状态。
/*! 线程池的属性。用于设置和修改线程池的参数。 */
typedef struct THREADPOOLATTR
{
    /*! 线程池始终至少保持这个数量的线程。 */
    int minThreads;
    /*! 线程池的线程数永远不会超过此数量。 */
    int maxThreads;
    /*! 分配给每个线程的最小栈大小。 */
    size_t stackSize;
    /*! 线程在终止前保持空闲的最长时间(以毫秒为单位)。 */
    int maxIdleTime;
    /*! 每个线程维护的任务数。 */
    int jobsPerThread;
    /*! 总共可以排队的最大任务数量。 */
    int maxJobsTotal;
    /*! 低优先级或中等优先级的任务在被提升优先级之前的等待时间(以毫秒为单位)。 */
    int starvationTime;
    /*! 使用的调度策略。 */
    PolicyType schedPolicy;
} ThreadPoolAttr;

/*! 内部线程池任务。 */
typedef struct THREADPOOLJOB
{
    start_routine func;       // 任务的执行函数
    void *arg;                // 传递给任务函数的参数
    free_routine free_func;   // 释放资源的回调函数
    struct timeval requestTime;  // 任务请求的时间
    ThreadPriority priority;  // 任务优先级
    int jobId;                // 任务ID
} ThreadPoolJob;

/*! 用于存储统计数据的结构体。 */
typedef struct TPOOLSTATS
{
    double totalTimeHQ;    // 高优先级任务的总处理时间
    int totalJobsHQ;       // 已处理的高优先级任务总数
    double avgWaitHQ;      // 高优先级任务的平均等待时间
    double totalTimeMQ;    // 中等优先级任务的总处理时间
    int totalJobsMQ;       // 已处理的中等优先级任务总数
    double avgWaitMQ;      // 中等优先级任务的平均等待时间
    double totalTimeLQ;    // 低优先级任务的总处理时间
    int totalJobsLQ;       // 已处理的低优先级任务总数
    double avgWaitLQ;      // 低优先级任务的平均等待时间
    double totalWorkTime;  // 总工作时间
    double totalIdleTime;  // 总空闲时间
    int workerThreads;     // 工作线程的数量
    int idleThreads;       // 空闲线程的数量
    int persistentThreads; // 持久线程的数量
    int totalThreads;      // 线程总数
    int maxThreads;        // 最大线程数量
    int currentJobsHQ;     // 当前高优先级任务数
    int currentJobsLQ;     // 当前低优先级任务数
    int currentJobsMQ;     // 当前中等优先级任务数
} ThreadPoolStats;

/*!
 * \brief 一个类似于 UPnP SDK 中的线程池。
 *
 * 允许线程池中的线程调度和运行任务。线程池初始化时设置了最小和最大线程数、
 * 最大空闲时间以及每线程的任务比率。如果一个工作线程在最大空闲时间内未接收到任务,
 * 且当前线程池的运行线程数超过最小线程数,则该工作线程将退出。如果调度任务时,
 * 当前任务与线程的比率超过设定比率,且线程池的线程数少于最大值,则将创建一个新线程。
 */
typedef struct THREADPOOL
{
    /*! 保护任务队列的互斥锁。是对pthread_mutex_t(🔒对象)的封装 */
    ithread_mutex_t mutex;
    /*! 用于任务队列信号的条件变量。条件变量是一种允许线程等待特定条件的机制,直到满足某些条件才允许线程在访问共享资源之前进行访问,线程被唤醒,重新获取互斥锁并继续执行。https://blog.youkuaiyun.com/ResumeProject/article/details/129522548 */
    ithread_cond_t condition;
    /*! 用于启动和关闭的条件变量。 */
    ithread_cond_t start_and_shutdown;
    /*! 任务ID计数器。 */
    int lastJobId;
    /*! 标识线程池是否正在关闭。 */
    int shutdown;
    /*! 线程总数。 */
    int totalThreads;
    /*! 是否在等待新工作线程启动。 */
    int pendingWorkerThreadStart;
    /*! 当前正在执行任务的线程数量。 */
    int busyThreads;
    /*! 持久线程的数量。 */
    int persistentThreads;
    /*! 任务的空闲列表。 */
    FreeList jobFreeList;
    /*! 低优先级任务队列。 */
    LinkedList lowJobQ;
    /*! 中等优先级任务队列。 */
    LinkedList medJobQ;
    /*! 高优先级任务队列。 */
    LinkedList highJobQ;
    /*! 持久任务。 */
    ThreadPoolJob *persistentJob;
    /*! 线程池的属性。 */
    ThreadPoolAttr attr;
    /*! 统计数据。 */
    ThreadPoolStats stats;
} ThreadPool;

UpnpInitThreadPools

  • UpnpInitThreadPools()(无参数)为预初始化直接调用的函数。
/*!
 * \brief Initializes the global threadm pools used by the UPnP SDK.
 *
 * \return UPNP_E_SUCCESS on success or UPNP_E_INIT_FAILED if a mutex could not
 * 	be initialized.
 */
static int UpnpInitThreadPools(void)
{
	int ret = UPNP_E_SUCCESS;
	ThreadPoolAttr attr;

	TPAttrInit(&attr); // 初始化属性,然后设置线程池的最大最小线程数,每个线程的JOB数量等等
	TPAttrSetMaxThreads(&attr, MAX_THREADS);
	TPAttrSetMinThreads(&attr, MIN_THREADS);
	TPAttrSetStackSize(&attr, THREAD_STACK_SIZE);
	TPAttrSetJobsPerThread(&attr, JOBS_PER_THREAD);
	TPAttrSetIdleTime(&attr, THREAD_IDLE_TIME);
	TPAttrSetMaxJobsTotal(&attr, MAX_JOBS_TOTAL);

	// 然后利用以上属性初始化三个全局线程池
	if (ThreadPoolInit(&gSendThreadPool, &attr) != UPNP_E_SUCCESS) {
		ret = UPNP_E_INIT_FAILED;
		goto exit_function;
	}

	if (ThreadPoolInit(&gRecvThreadPool, &attr) != UPNP_E_SUCCESS) {
		ret = UPNP_E_INIT_FAILED;
		goto exit_function;
	}

	if (ThreadPoolInit(&gMiniServerThreadPool, &attr) != UPNP_E_SUCCESS) {
		ret = UPNP_E_INIT_FAILED;
		goto exit_function;
	}

exit_function:
	if (ret != UPNP_E_SUCCESS) {
		UpnpSdkInit = 0;
		UpnpFinish();
	}

	return ret;
}

函数ThreadPoolInit初始化并启动线程池

函数声明

// https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/threadutil/ThreadPool.h#L252-L280
/*!
 * \brief 初始化并启动线程池。必须首先调用此函数,并且只能调用一次以初始化线程池。
 *
 * \return
 * \li \c 0 表示成功。
 * \li \c EAGAIN 表示系统资源不足,无法创建最小数量的线程。
 * \li \c INVALID_POLICY 表示无法设置调度策略。
 * \li \c EMAXTHREADS 表示最小线程数大于最大线程数。
 */
int ThreadPoolInit(
	/*! 必须是有效的、非空的 ThreadPool 指针。 */
	ThreadPool *tp,
	/*! 可以为 NULL。如果不为 NULL,则 attr 包含以下字段:
	 * \li \c minWorkerThreads - 线程池中最少的工作线程数量,线程池不会少于这个数量的线程。
	 * \li \c maxWorkerThreads - 线程池中最多的工作线程数量,线程池不会超过这个数量的线程。
	 * \li \c maxIdleTime - 工作线程保持空闲的最长时间。如果线程空闲时间超过此值且
	 * 运行中的线程数超过最小数量,则该工作线程退出。
	 * \li \c jobsPerThread - 每个线程要维护的任务与线程的比率。如果调度任务时每个线程的任务
	 * 数超过这个比率,并且运行中的工作线程数少于最大线程数,则启动一个新线程以提高效率。
	 * \li \c schedPolicy - 尝试设置的调度策略(依赖于操作系统)。
	 */
	ThreadPoolAttr *attr);

函数定义

  • 先进行线程池加锁,然后初始化线程池tp,后续解锁。
    在这里插入图片描述
// https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/threadutil/ThreadPool.c#L710-L776
int ThreadPoolInit(ThreadPool *tp, ThreadPoolAttr *attr)
{
    int retCode = 0; // 用于存储返回的错误值
    int i = 0; // 计数器

    // 检查线程池指针是否为空,如果为空则返回错误代码 EINVAL
    if (!tp) {
        return EINVAL;
    }

    // 初始化线程池的互斥锁,并将其加锁
    retCode += ithread_mutex_init(&tp->mutex, NULL);// pthread_mutex_init
    retCode += ithread_mutex_lock(&tp->mutex);

    // 初始化线程池的条件变量
    retCode += ithread_cond_init(&tp->condition, NULL);
    retCode += ithread_cond_init(&tp->start_and_shutdown, NULL);

    // 如果初始化失败,解锁并销毁互斥锁和条件变量,返回错误代码 EAGAIN
    if (retCode) {
        ithread_mutex_unlock(&tp->mutex);
        ithread_mutex_destroy(&tp->mutex);
        ithread_cond_destroy(&tp->condition);
        ithread_cond_destroy(&tp->start_and_shutdown);
        return EAGAIN;
    }

    // 如果提供了线程池属性,则使用该属性,否则初始化默认属性
    if (attr) {
        tp->attr = *attr;
    } else {
        TPAttrInit(&tp->attr);
    }

    // 检查并设置调度策略(和操作系统相关,[sched_getparam 手册页](https://man7.org/linux/man-pages/man2/sched_getparam.2.html))
    if (SetPolicyType(tp->attr.schedPolicy) != 0) {
        // 如果设置失败,则返回错误代码 INVALID_POLICY
        ithread_mutex_unlock(&tp->mutex);
        ithread_mutex_destroy(&tp->mutex);
        ithread_cond_destroy(&tp->condition);
        ithread_cond_destroy(&tp->start_and_shutdown);
        return INVALID_POLICY;
    }

    // 初始化线程池的作业空闲列表和统计信息
    retCode += FreeListInit(&tp->jobFreeList, sizeof(ThreadPoolJob), JOBFREELISTSIZE);
    StatsInit(&tp->stats);

    // 初始化线程池的高、中、低优先级作业队列
    retCode += ListInit(&tp->highJobQ, CmpThreadPoolJob, NULL);
    retCode += ListInit(&tp->medJobQ, CmpThreadPoolJob, NULL);
    retCode += ListInit(&tp->lowJobQ, CmpThreadPoolJob, NULL);

    // 如果初始化失败,则返回 EAGAIN 错误代码
    if (retCode) {
        retCode = EAGAIN;
    } else {
        // 初始化线程池的作业和线程计数信息
        tp->persistentJob = NULL;
        tp->lastJobId = 0;
        tp->shutdown = 0;
        tp->totalThreads = 0;
        tp->busyThreads = 0;
        tp->persistentThreads = 0;
        tp->pendingWorkerThreadStart = 0;

        // ********** 创建最小数量的线程 **********
        for (i = 0; i < tp->attr.minThreads; ++i) {
            retCode = CreateWorker(tp); // 创建一个工作线程
            if (retCode) {
                break;
            }
        }
    }

    // 解锁互斥锁
    ithread_mutex_unlock(&tp->mutex);

    // 如果线程创建失败,则清理线程池并关闭
    if (retCode) {
        /* 如果无法创建最小线程数,进行清理 */
        ThreadPoolShutdown(tp);
    }

    return retCode; // 返回结果代码,0 表示成功
}

CreateWorker函数:创建工作线程

/*! 
 * \brief Creates a worker thread, if the thread pool does not already have
 * max threads.
 *
 * 创建一个工作线程,如果线程池中的线程数尚未达到最大线程数。
 *
 * \remark The ThreadPool object mutex must be locked prior to calling this
 * function.
 *
 * 调用此函数之前,必须锁定 ThreadPool 对象的互斥锁(mutex)。
 *
 * \internal
 *
 * \return
 *	\li \c 0 on success, < 0 on failure.
 *	\li 成功返回 0,失败时返回小于 0 的值。
 *	\li \c EMAXTHREADS if already max threads reached.
 *	\li 如果已经达到最大线程数,返回 EMAXTHREADS。
 *	\li \c EAGAIN if system can not create thread.
 *	\li 如果系统无法创建新线程,返回 EAGAIN。
 */
static int CreateWorker(
	/*! A pointer to the ThreadPool object. */
	// 指向 ThreadPool 对象的指针。
	ThreadPool *tp)
{
	// 临时变量,用于存储新创建线程的句柄。
	ithread_t temp;
	
	// 返回代码初始化为 0,表示成功。
	int rc = 0;
	
	// 线程属性结构体,用于设置新线程的属性。
	ithread_attr_t attr;

	/* if a new worker is the process of starting, wait until it fully
	 * starts
	 *
	 * 如果一个新的工作线程正在启动过程中,等待它完全启动。
	 */
	while (tp->pendingWorkerThreadStart) {
		// 等待条件变量,直到新的线程完成启动。
		ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex);
	}

	// 检查是否已达到最大线程数
	if (tp->attr.maxThreads != INFINITE_THREADS &&
		tp->totalThreads + 1 > tp->attr.maxThreads) {
		// 如果线程数超过最大线程数,返回 EMAXTHREADS 错误。
		return EMAXTHREADS;
	}

	// 初始化线程属性
	ithread_attr_init(&attr);
	// 设置线程栈大小为线程池中指定的大小。
	ithread_attr_setstacksize(&attr, tp->attr.stackSize);
	// 设置线程为分离状态(Detached),表示该线程结束后资源会自动释放。
	ithread_attr_setdetachstate(&attr, ITHREAD_CREATE_DETACHED);

	// 创建新线程,并将新线程与 WorkerThread 函数关联。线程属性通过 attr 设置,tp 是传递给新线程的参数。
	rc = ithread_create(&temp, &attr, WorkerThread, tp);

	// 销毁线程属性对象,释放资源。
	ithread_attr_destroy(&attr);

	// 如果线程创建成功
	if (rc == 0) {
		// 设置标志,表示新的工作线程正在启动。
		tp->pendingWorkerThreadStart = 1;

		// 等待新线程启动完成。创建的工作线程会在准备好后将线程池的标志置为0
		while (tp->pendingWorkerThreadStart) {
			ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex);
		}
	}

	// 更新统计信息,如果当前线程数超过之前的最大线程数,则更新最大线程数。
	if (tp->stats.maxThreads < tp->totalThreads) {
		tp->stats.maxThreads = tp->totalThreads;
	}

	// 返回创建线程的结果,0 表示成功,非 0 表示失败。
	return rc;
}

WorkerThread

  • 代码rc = ithread_create(&temp, &attr, WorkerThread, tp);会创建一个新的线程,并运行WorkerThread函数。
    在这里插入图片描述

下面是对 WorkerThread 函数逐行的注释,解释其工作原理。

/*!
 * \brief Implements a thread pool worker. Worker waits for a job to become
 * available. Worker picks up persistent jobs first, high priority,
 * med priority, then low priority.
 *
 * 实现线程池工作者线程的逻辑。工作者线程等待作业可用,优先处理持久性作业,
 * 然后按优先级高、中、低的顺序处理其他作业。
 *
 * If worker remains idle for more than specified max, the worker is released.
 *
 * 如果工作者线程空闲时间超过指定的最大值,则该线程会被释放。
 *
 * \internal
 */
static void *WorkerThread(
	/*! arg -> is cast to (ThreadPool *). */
	// 参数 `arg` 会被转换为 `ThreadPool *` 类型
	void *arg)
{
	// 记录线程开始时间的变量
	time_t start = 0;

	// 指向线程池中的作业的指针,初始为 NULL
	ThreadPoolJob *job = NULL;
	// 列表中的节点,用于从作业队列中提取作业
	ListNode *head = NULL;

	// 用于超时等待的时间结构体
	struct timespec timeout;
	// 返回代码,初始化为 0
	int retCode = 0;
	// 表示是否是持久线程的标志
	int persistent = -1;
	// 将传入的参数 `arg` 转换为 `ThreadPool *`
	ThreadPool *tp = (ThreadPool *)arg;

	// 初始化线程库(根据平台的需要,linux可能是空操作)
	ithread_initialize_thread();

	/* Increment total thread count */
	// 增加线程池中线程的总数,并通知其它等待线程该线程已启动
	ithread_mutex_lock(&tp->mutex);
	tp->totalThreads++;
	
	tp->pendingWorkerThreadStart = 0;// 标记线程不再是待启动状态
	// 广播信号,通知所有等待在条件变量 `start_and_shutdown` 上的线程
	ithread_cond_broadcast(&tp->start_and_shutdown);
	ithread_mutex_unlock(&tp->mutex);

	// 设置随机种子,通常用于某些随机性需求的任务
	SetSeed();
	// 获取当前时间,用于计算线程的工作和空闲时间
	StatsTime(&start);
	while (1) {
		// 获取线程池的互斥锁,确保对共享数据的安全访问
		ithread_mutex_lock(&tp->mutex);
		if (job) {
			// 如果有作业在执行,作业完成后减少忙碌线程数,并释放作业
			tp->busyThreads--;
			FreeThreadPoolJob(tp, job);
			job = NULL;
		}
		retCode = 0;
		// 增加空闲线程数
		tp->stats.idleThreads++;
		// 更新总工作时间
		tp->stats.totalWorkTime += (double)StatsTime(NULL) - (double)start;
		// 重新获取当前时间,准备记录空闲时间
		StatsTime(&start);
		if (persistent == 0) {
			// 如果当前线程不是持久线程,减少线程池中工作线程的计数
			tp->stats.workerThreads--;
		} else if (persistent == 1) {
			// 如果是持久线程,则将其转为普通线程
			tp->persistentThreads--;
		}

		/* Check for a job or shutdown */
		// 如果所有优先级队列中都没有作业且没有持久作业,同时线程池未被关闭,则进入等待
		while (tp->lowJobQ.size == 0 && tp->medJobQ.size == 0 &&
			   tp->highJobQ.size == 0 && !tp->persistentJob &&
			   !tp->shutdown) {
			// 如果等待超时并且总线程数超过最小线程数,或者线程数超过最大线程数,释放当前线程
			if ((retCode == ETIMEDOUT && tp->totalThreads > tp->attr.minThreads) ||
				(tp->attr.maxThreads != -1 && tp->totalThreads > tp->attr.maxThreads)) {
				tp->stats.idleThreads--;
				goto exit_function;
			}
			// 设置相对超时时间
			SetRelTimeout(&timeout, tp->attr.maxIdleTime);

			/* wait for a job up to the specified max time */
			// 在超时时间内等待新作业的到来
			retCode = ithread_cond_timedwait(&tp->condition, &tp->mutex, &timeout);
		}
		// 减少空闲线程数
		tp->stats.idleThreads--;
		// 更新总空闲时间
		tp->stats.totalIdleTime += (double)StatsTime(NULL) - (double)start;
		// 重新获取当前时间,准备记录工作时间
		StatsTime(&start);
		// 提升饥饿作业的优先级
		BumpPriority(tp);
		// 如果线程池已关闭,退出循环
		if (tp->shutdown) {
			goto exit_function;
		} else {
			// 如果有持久作业,优先处理它
			if (tp->persistentJob) {
				job = tp->persistentJob;
				tp->persistentJob = NULL;
				tp->persistentThreads++;
				persistent = 1;
				ithread_cond_broadcast(&tp->start_and_shutdown);
			} else {
				// 没有持久作业时,从高优先级队列中取作业
				tp->stats.workerThreads++;
				persistent = 0;
				// 依次尝试从高、中、低优先级队列中取作业
				if (tp->highJobQ.size > 0) {
					head = ListHead(&tp->highJobQ);
					if (head == NULL) {
						tp->stats.workerThreads--;
						goto exit_function;
					}
					job = (ThreadPoolJob *)head->item;
					CalcWaitTime(tp, HIGH_PRIORITY, job);
					ListDelNode(&tp->highJobQ, head, 0);
				} else if (tp->medJobQ.size > 0) {
					head = ListHead(&tp->medJobQ);
					if (head == NULL) {
						tp->stats.workerThreads--;
						goto exit_function;
					}
					job = (ThreadPoolJob *)head->item;
					CalcWaitTime(tp, MED_PRIORITY, job);
					ListDelNode(&tp->medJobQ, head, 0);
				} else if (tp->lowJobQ.size > 0) {
					head = ListHead(&tp->lowJobQ);
					if (head == NULL) {
						tp->stats.workerThreads--;
						goto exit_function;
					}
					job = (ThreadPoolJob *)head->item;
					CalcWaitTime(tp, LOW_PRIORITY, job);
					ListDelNode(&tp->lowJobQ, head, 0);
				} else {
					// 理论上不应到达此处,若到达则表示出错
					tp->stats.workerThreads--;
					goto exit_function;
				}
			}
		}

		// 增加忙碌线程数
		tp->busyThreads++;
		// 释放互斥锁,允许其他线程访问共享资源
		ithread_mutex_unlock(&tp->mutex);

		/* In the future can log info */
		// 设置作业的优先级(如果需要)
		if (SetPriority(job->priority) != 0) {
			// 处理设置优先级失败的情况
		} else {
			// 成功设置优先级后的逻辑
		}
		// 执行作业
		job->func(job->arg);
		// 恢复为默认优先级
		SetPriority(DEFAULT_PRIORITY);
	}

exit_function:
	// 减少线程池中的总线程数,并通知其他等待的线程
	tp->totalThreads--;
	ithread_cond_broadcast(&tp->start_and_shutdown);
	// 释放互斥锁
	ithread_mutex_unlock(&tp->mutex);
	// 清理线程的资源
	ithread_cleanup_thread();

	return NULL;
}
  • 功能:该函数实现了线程池中的工作线程逻辑。工作线程等待作业到来,优先处理持久作业,并按照优先级从高到低处理普通作业。如果线程池空闲时间过长或线程数超过限制,工作线程会自行销毁。

  • 线程同步:通过互斥锁和条件变量实现线程间的同步,确保线程安全地访问共享资源。

  • 优先级处理:线程首先处理持久作业,然后根据优先级依次处理高、中、低优先级的作业。

  • TPJobInit(&job, (start_routine)AutoAdvertise, adEvent);可添加JOB,以下是WorkerThread对JOB的执行。
    在这里插入图片描述
    在这里插入图片描述

相关任务队列

1. 任务的空闲列表(Free List of Jobs)
  • 当任务执行完成后,任务对象会被回收到空闲列表中。
  • 当线程池需要分配新任务时,如果空闲列表中存在可用的任务对象,则直接从空闲列表中取出并复用,而不需要重新创建任务对象。
  • 这样可以提高线程池的效率,尤其在大量短任务需要频繁创建和销毁时。
2. 低优先级任务队列(Low Priority Job Queue)
  • 任务调度器会根据任务的优先级,将低优先级任务放入低优先级任务队列中。
  • 当所有高优先级和中等优先级的任务都处理完,且线程池中有空闲线程时,低优先级任务才会被分配给线程执行。
3. 中等优先级任务队列(Medium Priority Job Queue)
  • 中等优先级任务队列存储需要尽快执行的任务。
  • 在调度时,线程池会先处理高优先级任务,之后再处理中等优先级任务。
  • 如果线程池有可用的线程,并且没有高优先级任务等待,中等优先级任务会被调度执行。
4. 高优先级任务队列(High Priority Job Queue)
  • 高优先级任务在提交到线程池后,会被放入高优先级任务队列中。
  • 当线程池有空闲线程时,调度器会首先查看高优先级任务队列,并分配空闲线程来执行这些任务。
  • 高优先级任务可以抢占正在处理的低优先级或中等优先级任务。
5. 持久任务(Persistent Job)
  • 持久任务是一些需要持续运行的任务,通常不会被移除线程池,除非系统关闭或特殊条件触发。
  • 持久任务通常会在特定线程上执行,这些线程不会在空闲时销毁,而是始终保持活跃。
  • 持久任务在调度中可能具有较高的优先级,且通常绑定到特定线程上持续运行,不会被其他任务抢占。
If you are experiencing problems with the Universal Plug and Play service, your computer might not be able to automatically detect the presence of other networked devices, such as PCs, printers, Internet access points and so on. That is where the UPnP Test application comes in. This simple program is designed to help you identify the issues that prevent the UPnP protocol from functioning correctly. Before you get your hopes up, you should know that this tool does not solve the detected problems, but only performs a series of tests to identify the possible causes. One advantage is that the application does not require installation, so your system registry is not affected in any way. The interface is compact and simple, comprising only two panels: one that displays the test type and its short description and the other for viewing which of the tests passed and which failed. The program can verify whether the operating system provides support for the UPnP service and allows you to check if the Simple Service Discovery Protocol (SSDP) and the UPnPHost services are running. It also verifies the connection between your network adapter and your router and the system's capacity to receive UPnP messages, as well as the router's capability to report an external IP address. One of the tests is designed to check if the Windows firewall service is blocking the traffic between your router and the system, thus preventing UPnP from working. The results can be copied to your clipboard by simply pressing a button and the tests can be redone easily. If you want to fix the detected issues, the link in the main interface can prove useful. In conclusion, UPnP Test is a simple tool for detecting problems related to device-to-device networking. However, it can only suggest possible reasons why the UPnP is not working, fixing the detected issues is totally up to you.
mips-linux-gnu-gcc -Os -pipe -march=mips32r2 -g -I/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/usr/lib/libiconv-full/include -I/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/usr/lib/libintl-full/include -DTAPO_USR_DEF_AUDIO_ALARM -DCONFIG_STATISTIC_REPORT_DOMAIN=n-da.tplinkcloud.com.cn -DTELEMETRY_SUPPORT -DPTZ_SUPPORT=gpio -DUPNPC_ENABLE -DOPTIONAL_ALARM_AUDIO -DMSG_PUSH_TO_ALEXA -DFACTORY_INFO_INTEGRITY_RESTORE_SUPPORT -DWLAN_MODULE_SUPPORT -DGZIP_SUPPORT -DBASE64_SUPPORT -DTELEMETRY_SUPPORT -DLOCAL_PROTOCOL_SECURE -DCLIENTS_CONN_MAX=4 -DHUB_MANAGE_SUPPORT -DCONFIG_MSG_PUSH_POST_URL=/api/v1 -DSENSITIVITY_INT -DMAKEROOM_BEFORE_UPGRADE -DAUDIO_ENABLE -DCONFIG_MOBILE_ACCESS_SET_SUPPORT -DCONFIG_SD_UPGRADE_FROM_MMCBLK0 -DSYSUPGRADE_CHECK_RSA -DCONFIG_TP_TAPO_SPMINIOS -DCONFIG_TP_TAPO_MAP_ROOTFS -DCDN_HTTPS_UPGRADE_SUPPORT -DAMAZON_FFS_SUPPORT -DTAPO_CARE_CDN_SUPPORT -DTP_TAPO -DCLOUD_IOT_SUPPORT -DCLOUD_IOT_SOUNDALARM_SUPPORT -DBIND_CODE_SUPPORT -DTP_TAPO_P2P_SHARE -DT43_CONFIG_CHECK_WHETHER_IS_CAL -DLENS_MASK_SUPPORT -DMULTI_MODEL -I/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/usr/include -I/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/include -I/home/wyl/Documents/NVMP/nvmp/../sdk/soc/t31x/uclibc-toolchain-0.9.33/mips-gcc472-glibc216-64bit/mips-linux-gnu/libc/uclibc/usr/include -I/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/usr/lib/libiconv-full/include -I/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/usr/lib/libintl-full/include -Wall -Werror -ffunction-sections -fdata-sections -DMODULE_LIST="\"tdpd tdpc tmpd mactool wirelesstool nifc dhcpc dhcps httpd sntpc onvif system miniupnpc upgrade telemetry cloud_iot remote_debugger hub_manage ffs\"" -I/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/include -I/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/modules -I/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/common -I/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/common/ds -I./libXml -I./libutils -I../../include -c -o hub_manage.o hub_manage.c hub_manage.c: In function 'set_hub_upnp_info': hub_manage.c:1279:3: error: implicit declaration of function 'UPNPC_ERROR' [-Werror=implicit-function-declaration] hub_manage.c:1311:3: error: implicit declaration of function 'sync_hub_timer_handler' [-Werror=implicit-function-declaration] hub_manage.c:1312:3: error: implicit declaration of function 'UPNPC_DEBUG' [-Werror=implicit-function-declaration] cc1: all warnings being treated as errors <builtin>: recipe for target 'hub_manage.o' failed make[5]: *** [hub_manage.o] Error 1 make[5]: Leaving directory '/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/modules/hub_manage' Makefile:30: recipe for target 'build_modules' failed make[4]: *** [build_modules] Error 2 make[4]: Leaving directory '/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd' Makefile:574: recipe for target '/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/.built' failed make[3]: *** [/home/wyl/Documents/NVMP/nvmp/build_dir/target-mips-openwrt-linux-uclibc-c510wv1/nsd/.built] Error 2 make[3]: Leaving directory '/home/wyl/Documents/NVMP/nvmp/tp_package/nsd' package/Makefile:105: recipe for target 'package/tp_package/nsd/compile' failed make[2]: *** [package/tp_package/nsd/compile] Error 2 make[2]: Leaving directory '/home/wyl/Documents/NVMP/nvmp' package/Makefile:101: recipe for target '/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/stamp/.package_compile' failed make[1]: *** [/home/wyl/Documents/NVMP/nvmp/staging_dir/target-mips-openwrt-linux-uclibc-c510wv1/stamp/.package_compile] Error 2 make[1]: Leaving directory '/home/wyl/Documents/NVMP/nvmp' /home/wyl/Documents/NVMP/nvmp/include/toplevel.mk:277: recipe for target 'world' failed make: *** [world] Error 2
最新发布
10-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值