x265-1.8版本-common/threading.h注释

注:问号以及未注释部分 会在x265-1.9版本内更新

/*****************************************************************************
 * Copyright (C) 2013 x265 project
 *
 * Authors: Steve Borho <steve@borho.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
 *
 * This program is also available under a commercial proprietary license.
 * For more information, contact us at license @ x265.com
 *****************************************************************************/

#ifndef X265_THREADING_H
#define X265_THREADING_H

#include "common.h"
#include "x265.h"

#ifdef _WIN32
#include <windows.h>
#include "winxp.h"  // XP workarounds for CONDITION_VARIABLE and ATOMIC_OR
#else
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#endif

#if MACOS
#include <sys/param.h>
#include <sys/sysctl.h>
#endif

#if NO_ATOMICS

#include <sys/time.h>
#include <unistd.h>

namespace X265_NS {
// x265 private namespace
int no_atomic_or(int* ptr, int mask);
int no_atomic_and(int* ptr, int mask);
int no_atomic_inc(int* ptr);
int no_atomic_dec(int* ptr);
int no_atomic_add(int* ptr, int val);
}

#define CLZ(id, x)            id = (unsigned long)__builtin_clz(x) ^ 31
#define CTZ(id, x)            id = (unsigned long)__builtin_ctz(x)
#define ATOMIC_OR(ptr, mask)  no_atomic_or((int*)ptr, mask)
#define ATOMIC_AND(ptr, mask) no_atomic_and((int*)ptr, mask)
#define ATOMIC_INC(ptr)       no_atomic_inc((int*)ptr)
#define ATOMIC_DEC(ptr)       no_atomic_dec((int*)ptr)
#define ATOMIC_ADD(ptr, val)  no_atomic_add((int*)ptr, val)
#define GIVE_UP_TIME()        usleep(0)

#elif __GNUC__               /* GCCs builtin atomics */

#include <sys/time.h>
#include <unistd.h>

#define CLZ(id, x)            id = (unsigned long)__builtin_clz(x) ^ 31
#define CTZ(id, x)            id = (unsigned long)__builtin_ctz(x)
#define ATOMIC_OR(ptr, mask)  __sync_fetch_and_or(ptr, mask)
#define ATOMIC_AND(ptr, mask) __sync_fetch_and_and(ptr, mask)
#define ATOMIC_INC(ptr)       __sync_add_and_fetch((volatile int32_t*)ptr, 1)
#define ATOMIC_DEC(ptr)       __sync_add_and_fetch((volatile int32_t*)ptr, -1)
#define ATOMIC_ADD(ptr, val)  __sync_fetch_and_add((volatile int32_t*)ptr, val)
#define GIVE_UP_TIME()        usleep(0)

#elif defined(_MSC_VER)       /* Windows atomic intrinsics */

#include <intrin.h>

#define CLZ(id, x)            _BitScanReverse(&id, x)// 返回数值x最高位1的位置(从0开始计数)如 0:0 1:0  2(10):1 3(11):1 4(100):1 5(101):2 6(110):2  7(111):2 8(1000):3
#define CTZ(id, x)            _BitScanForward(&id, x)// 返回数值x从低位起 有多个连续0的个数  如 0:0 1:0  2(10):1 3(11):0 4(100):2 5(101):0 6(110):1  7(111):0 8(1000):3
#define ATOMIC_INC(ptr)       InterlockedIncrement((volatile LONG*)ptr) //原子锁,自加一  返回值是ptr原先的值  然后更新ptr的值
#define ATOMIC_DEC(ptr)       InterlockedDecrement((volatile LONG*)ptr) //原子锁,自减一  返回值是ptr原先的值  然后更新ptr的值
#define ATOMIC_ADD(ptr, val)  InterlockedExchangeAdd((volatile LONG*)ptr, val)
#define ATOMIC_OR(ptr, mask)  _InterlockedOr((volatile LONG*)ptr, (LONG)mask)//原子锁,或操作    返回值是ptr原先的值  然后更新ptr的值
#define ATOMIC_AND(ptr, mask) _InterlockedAnd((volatile LONG*)ptr, (LONG)mask)//原子锁,与操作  返回值是ptr原先的值  然后更新ptr的值
#define GIVE_UP_TIME()        Sleep(0)

#endif // ifdef __GNUC__

namespace X265_NS {
// x265 private namespace

#ifdef _WIN32

typedef HANDLE ThreadHandle;

class Lock //用于加锁 被Lookahead、BondedTaskGroup所使用
{
public:

    Lock()
    {
        InitializeCriticalSection(&this->handle);
        /*
        VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection ):函数功能初始化一个临界资源对象。“临界区”CCriticalSection [1] 是临界资源对象指针。该函数无返回值。单进程的各个线程可以使用临界资源对象来解决同步互斥问题,该对象不能保证哪个线程能够获得到临界资源对象,该系统能公平的对待每一个线程
        **/
    }

    ~Lock()
    {
        DeleteCriticalSection(&this->handle);//删除临界资源变量
    }

    void acquire()
    {
        EnterCriticalSection(&this->handle);
        /*
        函数EnterCriticalSection和LeaveCriticalSection中间的代码执行过程不会被其他线程干拢或者这么讲不允许其他线程中  的代码执行。这样可以有效防止一个全局变量在两个线程中同时被操作的可能性
        **/
    }

    void release()
    {
        LeaveCriticalSection(&this->handle);//同上
    }

protected:

    CRITICAL_SECTION handle;
    /*
    不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。每个线程中访问临界资源的那段代码称为临界区(Critical Section)。
    每个线程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。
    多个线程中涉及到同一个临界资源的临界区称为相关临界区。
    线程进入临界区的调度原则是: ①如果有若干线程要求进入空闲的临界区,一次仅允许一个线程进入。②任何时候,处于临界区内的线程不可多于一个。如已有线程进入自己的临界区,则其它所有试图进入临界区的线程必须等待。③进入临界区的线程要在有限时间内退出,以便其它线程能及时进入自己的临界区。④如果线程不能进入自己的临界区,则应让出CPU,避免线程出现“忙等”现象。
    临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。
    **/
};

class Event //只在FrameEncoder和Lookahead中应用
{
public:

    Event()
    {
        this->handle = CreateEvent(NULL, FALSE, FALSE, NULL);
        /*CreateEvent是一个Windows API函数。它用来创建或打开一个命名的或无名的事件对象
        分别为:安全属性、 复位方式、初始状态、对象名称
        安全属性:是NULL,此句柄不能被继承。
        复位方式: 指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。
        初始状态:指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
        对象名称:如果lpName为NULL,将创建一个无名的事件对象。
        **/
    }

    ~Event()
    {
        CloseHandle(this->handle);
        /*关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
        若在线程执行完之后,没有调用CloseHandle,在进程执行期间,将会造成内核对象的泄露,相当于句柄泄露,但不同于内存泄露,这势必会对系统的效率带来一定程度上的负面影响。但当进程结束退出后,系统会自动清理这些资源。
        **/
    }

    void wait()
    {
        WaitForSingleObject(this->handle, INFINITE);
        /*
        dwMilliseconds[in]定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。
        **/
    }

    bool timedWait(uint32_t milliseconds)//超时返回true
    {
        /* returns true if the wait timed out */
        return WaitForSingleObject(this->handle, milliseconds) == WAIT_TIMEOUT; //表示在milliseconds时间内就会触发   如果等待超时返回true
    }

    void trigger()//触发
    {
        SetEvent(this->handle);//有信号是触发, 因为我们设置的是自动,一旦触发完毕立马变为false
        /*
        线程中SetEvent及WaitForSingleObject用法
        SetEvent/ResetEvent分别将EVENT置为这两种状态分别是发信号与不发信号。
        WaitForSingleObject()等待,直到参数所指定的OBJECT成为发信号状态时才返回,OBJECT可以是EVENT,也可以是其它内核对象。 当你创建一个线程时,其实那个线程是一个循环,不像上面那样只运行一次的。这样就带来了一个问题,在那个死循环里要找到合适的条件退出那个死循环,那么是怎么样实现它的呢,在Windows里往往是采用事件的方式,当然还可以采用其它的方式。在这里先介绍采用事件的方式来通知从线程运行函数退出来,它的实现原理是这样,在那个死循环里不断地使用WaitForSingleObject函数来检查事件是否满足,如果满足就退出线程,不满足就继续运行。当在线程里运行阻塞的函数时,就需要在退出线程时,先要把阻塞状态变成非阻塞状态,比如使用一个线程去接收网络数据,[2] 同时使用阻塞的SOCKET时,那么要先关闭SOCKET,再发送事件信号,才可以退出线程的。
        当然我感觉重要应用方面还是用来锁定,实现所谓的pv功能。
        在调用的过程中,所有线程都可以在一个等待函数中指定事件对象句柄。当指定的对象的状态被置为有信号状态时,单对象等待函数将返回。
        对于多对象等待函数,可以指定为任意或所有指定的对象被置为有信号状态。当等待函数返回时,等待线程将被释放去继续运行。
        初始状态在bInitialState参数中进行设置。使用SetEvent函数将事件对象的状态置为有信号状态。使用ResetEvent函数将事件对象的状态置为无信号状态。
        当一个手动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无符号状态。
        当事件的对象被置为有信号状态时,任意数量的等待中线程,以及随后开始等待的线程均会被释放。
        当一个自动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待,事件对象的状态将保持有信号状态。
        多个进程可持有同一个事件对象的多个句柄,可以通过使用此对象来实现进程间的同步。下面的对象共享机制是可行的:
        ·在CreateEvent函数中,lpEventAttributes参数指定句柄可被继承时,通过CreateProcess函数创建的子进程继承的事件对象句柄。
        ·一个进程可以在DuplicateHandle函数中指定事件对象句柄,从而获得一个复制的句柄,此句柄可以被其它进程使用。
        ·一个进程可以在OpenEvent或CreateEvent函数中指定一个名字,从而获得一个有名的事件对象句柄。
        使用CloseHandle函数关闭句柄。当进程停止时,系统将自动关闭句柄。当最后一个句柄被关闭后,事件对象将被销毁。
        **/
    }

protected:

    HANDLE handle;//句柄
    //线程一只是循环,开始设定为无信号,一旦有信号,则立即触发,如果设置为自动,触发完毕立即停止
};

/* This class is intended for use in signaling state changes safely between CPU
 * cores. One thread should be a writer and multiple threads may be readers. The
 * mutex's main purpose is to serve as a memory fence to ensure writes made by
 * the writer thread are visible prior to readers seeing the m_val change. Its
 * secondary purpose is for use with the condition variable for blocking waits */
class ThreadSafeInteger //用于防止多线程破坏的安全计数
{
public:
    /*我们已经看到,当想让写入者线程和读取者线程以独占的方式或共享的方式访问一个资源的时候,可以使用SRWLock。在这些情况下,如果读取者线程没有数据可以读取,那么它应该将锁释放并等待,直到写入者线程产生了新的数据为止。如果用来接收写入者线程产生的数据结构已满,那么写入者同样应该释放SRWLock并进入睡眠状态,直到读取这线程把数据结构清空为止。
    我们希望线程以原子的方式把锁释放并将自己阻塞,直到某一个条件成立为止。要实现这样的线程同步是比较复杂的。Windows通过SleepConditionVariableCS(critical section) 或者SleepConditionVariableSRW 函数,提供了一种 条件变量 帮助我们完成这项工作。
    当线程检测到相应的条件满足的时候(比如,有数据供读取者使用),它会调用 WakeConditionVariable 或  WakeAllConditionVariable,这样阻塞在Sleep*函数中的线程就会被唤醒。
    **/
    ThreadSafeInteger()//初始化
    {
        m_val = 0;
        InitializeCriticalSection(&m_cs);
        InitializeConditionVariable(&m_cv);
    }

    ~ThreadSafeInteger()//释放
    {
        DeleteCriticalSection(&m_cs);
        XP_CONDITION_VAR_FREE(&m_cv);
    }

    int waitForChange(int prev)//获取改变后的值,等到线程已经更新当前数据
    {
        /*
        参见linux函数pthread_mutex_lock
        如果互斥锁类型为 PTHREAD_MUTEX_NORMAL,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或未锁定,则将产生不确定的行为。
        如果互斥锁类型为 PTHREAD_MUTEX_ERRORCHECK,则会提供错误检查。如果某个线程尝试重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
        如果互斥锁类型为 PTHREAD_MUTEX_RECURSIVE,则该互斥锁会保留锁定计数这一概念。线程首次成功获取互斥锁时,锁定计数会设置为 1。线程每重新锁定该互斥锁一次,锁定计数就增加 1。线程每解除锁定该互斥锁一次,锁定计数就减小 1。 锁定计数达到 0 时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
        如果互斥锁类型是 PTHREAD_MUTEX_DEFAULT,则尝试以递归方式锁定该互斥锁将产生不确定的行为。对于不是由调用线程锁定的互斥锁,如果尝试解除对它的锁定,则会产生不确定的行为。如果尝试解除锁定尚未锁定的互斥锁,则会产生不确定的行为。
        PTHREAD_MUTEX_DEFAULT 描述: 如果尝试以递归方式锁定此类型的互斥锁,则会产生不确定的行为。对于不是由调用线程锁定的此类型互斥锁,如果尝试对它解除锁定,则会产生不确定的行为。对于尚未锁定的此类型互斥锁,如果尝试对它解除锁定,也会产生不确定的行为。允许在实现中将该互斥锁映射到其他互斥锁类型之一。
        等待条件有两种方式:条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
        无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
        激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。
        **/
        EnterCriticalSection(&m_cs);
        if (m_val == prev)
            SleepConditionVariableCS(&m_cv, &m_cs, INFINITE);
        LeaveCriticalSection(&m_cs);
        return m_val;
    }

    int get()//获取当前的值
    {
        EnterCriticalSection(&m_cs);
        int ret = m_val;
        LeaveCriticalSection(&m_cs);
        return ret;
    }

    void set(int newval)//设置数据
    {
        EnterCriticalSection(&m_cs);
        m_val = newval;
        WakeAllConditionVariable(&m_cv);
        LeaveCriticalSection(&m_cs);
    }

    void poke(void)//唤醒所有阻塞
    {
        /* awaken all waiting threads, but make no change */
        EnterCriticalSection(&m_cs);
        WakeAllConditionVariable(&m_cv);
        LeaveCriticalSection(&m_cs);
    }

    void incr()//累加1
    {
        EnterCriticalSection(&m_cs);
        m_val++;
        WakeAllConditionVariable(&m_cv);
        LeaveCriticalSection(&m_cs);
    }

protected:

    CRITICAL_SECTION   m_cs;//互斥量,锁
    CONDITION_VARIABLE m_cv;//状态参量
    int                m_val;//用于计数
};

#else /* POSIX / pthreads */

typedef pthread_t ThreadHandle;

class Lock
{
public:

    Lock()
    {
        pthread_mutex_init(&this->handle, NULL);
    }

    ~Lock()
    {
        pthread_mutex_destroy(&this->handle);
    }

    void acquire()
    {
        pthread_mutex_lock(&this->handle);
    }

    void release()
    {
        pthread_mutex_unlock(&this->handle);
    }

protected:

    pthread_mutex_t handle;
};

class Event
{
public:

    Event()
    {
        m_counter = 0;
        if (pthread_mutex_init(&m_mutex, NULL) ||
            pthread_cond_init(&m_cond, NULL))
        {
            x265_log(NULL, X265_LOG_ERROR, "fatal: unable to initialize conditional variable\n");
        }
    }

    ~Event()
    {
        pthread_cond_destroy(&m_cond);
        pthread_mutex_destroy(&m_mutex);
    }

    void wait()
    {
        pthread_mutex_lock(&m_mutex);

        /* blocking wait on conditional variable, mutex is atomically released
         * while blocked. When condition is signaled, mutex is re-acquired */
        while (!m_counter)
            pthread_cond_wait(&m_cond, &m_mutex);

        m_counter--;
        pthread_mutex_unlock(&m_mutex);
    }

    bool timedWait(uint32_t waitms)
    {
        bool bTimedOut = false;

        pthread_mutex_lock(&m_mutex);
        if (!m_counter)
        {
            struct timeval tv;
            struct timespec ts;
            gettimeofday(&tv, NULL);
            /* convert current time from (sec, usec) to (sec, nsec) */
            ts.tv_sec = tv.tv_sec;
            ts.tv_nsec = tv.tv_usec * 1000;

            ts.tv_nsec += 1000 * 1000 * (waitms % 1000);    /* add ms to tv_nsec */
            ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); /* overflow tv_nsec */
            ts.tv_nsec %= (1000 * 1000 * 1000);             /* clamp tv_nsec */
            ts.tv_sec += waitms / 1000;                     /* add seconds */

            /* blocking wait on conditional variable, mutex is atomically released
             * while blocked. When condition is signaled, mutex is re-acquired.
             * ts is absolute time to stop waiting */
            bTimedOut = pthread_cond_timedwait(&m_cond, &m_mutex, &ts) == ETIMEDOUT;
        }
        if (m_counter > 0)
        {
            m_counter--;
            bTimedOut = false;
        }
        pthread_mutex_unlock(&m_mutex);
        return bTimedOut;
    }

    void trigger()
    {
        pthread_mutex_lock(&m_mutex);
        if (m_counter < UINT_MAX)
            m_counter++;
        /* Signal a single blocking thread */
        pthread_cond_signal(&m_cond);
        pthread_mutex_unlock(&m_mutex);
    }

protected:

    pthread_mutex_t m_mutex;
    pthread_cond_t  m_cond;
    uint32_t        m_counter;
};

/* This class is intended for use in signaling state changes safely between CPU
 * cores. One thread should be a writer and multiple threads may be readers. The
 * mutex's main purpose is to serve as a memory fence to ensure writes made by
 * the writer thread are visible prior to readers seeing the m_val change. Its
 * secondary purpose is for use with the condition variable for blocking waits */
class ThreadSafeInteger
{
public:

    ThreadSafeInteger()
    {
        m_val = 0;
        if (pthread_mutex_init(&m_mutex, NULL) ||
            pthread_cond_init(&m_cond, NULL))
        {
            x265_log(NULL, X265_LOG_ERROR, "fatal: unable to initialize conditional variable\n");
        }
    }

    ~ThreadSafeInteger()
    {
        pthread_cond_destroy(&m_cond);
        pthread_mutex_destroy(&m_mutex);
    }

    int waitForChange(int prev)
    {
        pthread_mutex_lock(&m_mutex);
        if (m_val == prev)
            pthread_cond_wait(&m_cond, &m_mutex);
        pthread_mutex_unlock(&m_mutex);
        return m_val;
    }

    int get()
    {
        pthread_mutex_lock(&m_mutex);
        int ret = m_val;
        pthread_mutex_unlock(&m_mutex);
        return ret;
    }

    void set(int newval)
    {
        pthread_mutex_lock(&m_mutex);
        m_val = newval;
        pthread_cond_broadcast(&m_cond);
        pthread_mutex_unlock(&m_mutex);
    }

    void poke(void)
    {
        /* awaken all waiting threads, but make no change */
        pthread_mutex_lock(&m_mutex);
        pthread_cond_broadcast(&m_cond);
        pthread_mutex_unlock(&m_mutex);
    }

    void incr()
    {
        pthread_mutex_lock(&m_mutex);
        m_val++;
        pthread_cond_broadcast(&m_cond);
        pthread_mutex_unlock(&m_mutex);
    }

protected:

    pthread_mutex_t m_mutex;
    pthread_cond_t  m_cond;
    int             m_val;
};

#endif // ifdef _WIN32

class ScopedLock //用于局部锁
{
public:

    ScopedLock(Lock &instance) : inst(instance)
    {
        this->inst.acquire();
    }

    ~ScopedLock()
    {
        this->inst.release();
    }

protected:

    // do not allow assignments
    ScopedLock &operator =(const ScopedLock &);

    Lock &inst;//一个引用类型,用于临时变量操作所需要的lock 创建时,acquire,释放时 release
};

// Utility class which adds elapsed time of the scope of the object into the
// accumulator provided to the constructor
struct ScopedElapsedTime //用于统计时间的锁
{
    ScopedElapsedTime(int64_t& accum) : accumlatedTime(accum) { startTime = x265_mdate(); }

    ~ScopedElapsedTime() { accumlatedTime += x265_mdate() - startTime; }

protected:

    int64_t  startTime;
    int64_t& accumlatedTime;

    // do not allow assignments
    ScopedElapsedTime &operator =(const ScopedElapsedTime &);
};

//< Simplistic portable thread class.  Shutdown signalling left to derived class
class Thread //用于多线程 继承它的有:Y4MInput,YUVInput,FrameEncoder,WorkerThread  线程start 一般在encoder.create中可以找到
{
private:

    ThreadHandle thread;//类型: HANDLE 一个句柄
    /*
    HANDLE(句柄)是Windows操作系统中的一个概念。在Windows程序中,有各种各样的资源(窗口、图标、光标等),
    系统在创建这些资源时会为它们分配内存,并返回标示这些资源的标示号,即句柄。句柄指的是一个核心对象在某一个进程中的唯一索引,
    而不是指针。由于地址空间的限制,句柄所标识的内容对进程是不可见的,只能由操作系统通过进程句柄列表来进行维护。
    句柄列表:每个进程都要创建一个句柄列表,这些句柄指向各种系统资源,比如信号量,线程,和文件等,进程中的所有线程都可以访问这些资源。
    **/

public:

    Thread();

    virtual ~Thread();

    //< Derived class must implement ThreadMain.
    virtual void threadMain() = 0;//其子类定义,虚函数:Y4MInput,YUVInput,FrameEncoder,WorkerThread

    //< Returns true if thread was successfully created
    bool start();
    /*
    创建线程,其子类会调用,一般只在encoder中调用  并开始调用对应子类的 threadMain()
    内容:thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadShim, this, 0, &threadId);
    Windows API函数。该函数在主线程的基础上创建一个新线程。微软在Windows API中提供了建立新的线程的函数CreateThread。
    NULL,使用默认安全性,
    0,设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。
    (LPTHREAD_START_ROUTINE)ThreadShim: 指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:
    DWORD WINAPI 函数名 (LPVOID lpParam) ,格式不正确将无法调用成功。/但lpStartAddress要这样通过LPTHREAD_START_ROUTINE转换如: (LPTHREAD_START_ROUTINE)MyVoid
    this:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。
    dwCreationFlags :线程标志,可取值如下0:表示创建后立即激活。
    threadId:保存新线程的id。
    **/

    void stop();//stop,其子类会调用,一般只在encoder中调用
};
} // end namespace x265

#endif // ifndef X265_THREADING_H


 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值