今天我来带大家查看下微软是如何写线程控制函数的。当然实际中我们很少有机会写这样的代码,不过这里面用到的一些思想我们是可以利用的。就像我在前几篇文章中写到的
CreateEventW创建事件,InterlockedExchange原子操作,WaitForSingleObject等待信号,而新的东西就是这个SetPriority和SpinUntilBlocked我想对于已经了解线程的一些人从英文字面中已经可以看出这两个函数的作用了,我们只需要查看里面的内容就知道这些函数是什么作用了,其实就是一个决定线程优先级的函数。
BOOL SetThreadPriority(
HANDLE hThread, // handle to the thread
int nPriority // thread priority level
);
功能说明
设置指定线程的优先级
参数说明
hThread 要设置的线程句柄
nPriority 优先级别参数 可设置为一下参数
THREAD_PRIORITY_ABOVE_NORMAL 为比一般优先级高一个等级
THREAD_PRIORITY_BELOW_NORMAL 比一般低一个等级
THREAD_PRIORITY_HIGHEST 比一般高2个等级
THREAD_PRIORITY_IDLE
THREAD_PRIORITY_LOWEST 比一般低2个等级
THREAD_PRIORITY_NORMAL 一般等级
THREAD_PRIORITY_TIME_CRITICAL
我们继续向下面看发现可以看见线程在系统中是如何被挂起,执行,结束的这里面的操作理论可以参考我在前面多线程释放硬件的操作理论去理解。看了微软是如何编写线程控制的估计你在windows平台下就没有不能理解的线程控制了。
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// ThreadProxy.h
//
// Proxy for an OS context.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
namespace Concurrency
{
namespace details
{
class ThreadProxy : public Concurrency::IThreadProxy
{
public:
/// <summary>
/// Constructs a thread proxy.
/// </summary>
ThreadProxy(IThreadProxyFactory * pFactory, unsigned int stackSize);
/// <summary>
/// Destroys a thread proxy.
/// </summary>
virtual ~ThreadProxy();
/// <summary>
/// Retrieves a process unique id for the thread proxy.
/// </summary>
unsigned int GetId() const;
/// <summary>
/// Blocks the thread proxy until is is resumed via ResumeExecution or a different thread proxy switching to it.
/// </summary>
void SuspendExecution();
/// <summary>
/// Resumes execution of a thread proxy.
/// </summary>
void ResumeExecution();
/// <summary>
/// Spins until the 'this' thread proxy is in a firmly blocked state.
/// </summary>
/// <remarks>
/// This implements a sort of barrier. At certain points during execution, it is essential to wait until a thread proxy
/// has set the flag indicating it is blocked, in order to preserve correct behavior. One example is if there is a race
/// between block and unblock for the same proxy, i.e. if a thread proxy is trying to block at the same time a different
/// context is trying to unblock it.
/// </remarks>
void SpinUntilBlocked();
/// <summary>
/// Gets the priority of the thread proxy.
/// </summary>
int GetPriority() { return m_threadPriority; }
/// <summary>
/// Sets the priority of the underlying thread.
/// </summary>
/// <param name="priority">
/// The new priority value for the thread.
/// </param>
void SetPriority(int priority);
/// <summary>
/// Gets the stack size of the thread proxy. Multiply by 1 KB to get actual stack size in bytes.
/// </summary>
unsigned int GetStackSize() { return m_stackSize; }
/// <summary>
/// Cancels the thread proxy causing the underlying thread to exit.
/// </summary>
void Cancel();
/// <summary>
/// Returns the virtual processor root the thread proxy is running on.
/// </summary>
VirtualProcessorRoot * GetVirtualProcessorRoot() { return m_pRoot; }
/// <summary>
/// Sets the virtual processor root - used during affinitization.
/// </summary>
void SetVirtualProcessorRoot(VirtualProcessorRoot * pRoot) { m_pRoot = pRoot; }
/// <summary>
/// Returns a Win32 handle to the thread that is backing this proxy.
/// </summary>
HANDLE GetThreadHandle() { return m_hPhysicalContext; }
#if _DEBUG
// _DEBUG helper
DWORD GetThreadId() const { return m_threadId; }
#endif
protected:
// The thread proxy factory that created this thread proxy, and maintains the idle pool of thread proxies.
IThreadProxyFactory * m_pFactory;
// The OS handle for the underlying UT.
HANDLE m_hPhysicalContext;
// The blocking handle.
HANDLE m_hBlock;
// The virtual processor root on which this thread proxy is executing.
VirtualProcessorRoot *m_pRoot;
// Stores the stack size of the thread proxy. Multiply by 1 KB to get actual stack size in bytes.
unsigned int m_stackSize;
// Stores the last priority value that was set on the thread. Initial value is normal priority.
int m_threadPriority;
bool m_fSuspended;
volatile LONG m_fBlocked;
volatile LONG m_fCanceled;
private:
// Process wide unique identifier.
unsigned int m_id;
// Thread id.
DWORD m_threadId;
/// <summary>
/// Dispatch routine for thread proxies.
/// </summary>
virtual void Dispatch() = 0;
/// <summary>
/// Thread start routine for proxies.
/// </summary>
static DWORD CALLBACK ThreadProxyMain(LPVOID lpParameter);
};
} // namespace details
} // namespace Concurrency
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// ThreadProxy.cpp
//
// Proxy for an OS context.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "concrtinternal.h"
namespace Concurrency
{
namespace details
{
/// <summary>
/// Constructs a thread proxy.
/// </summary>
ThreadProxy::ThreadProxy(IThreadProxyFactory * pFactory, unsigned int stackSize) :
m_pFactory(pFactory),
m_stackSize(stackSize),
m_threadPriority(THREAD_PRIORITY_NORMAL),
m_fBlocked(TRUE),
m_fCanceled(FALSE),
m_fSuspended(false)
{
// Thread proxy factories for Win32 threads need to be reference counted by the individual thread proxies, unlike
// UMS based thread proxy factories. This is because thread proxies that were loaned out to threads based schedulers
// could still be executing their dispatch loop and about to put themselves onto the idle pool on the factory at
// the time that the schedulers and corresponding scheduler proxies are actually destroyed (and have removed their
// references on the RM). If no references exist on the RM, the RM goes aheads and destroys the factories. However,
// it is dangerous to do this while thread proxies are possibly in the process of returning to the factory. Therefore,
// the outstanding thread proxies (alive but not in the idle pool), need to keep the factory alive until they have all
// returned.
//
// UMS thread proxies on the other hand, need the existence of a UMS virtual processor root in order to execute, and the
// UMS virtual processor roots are responsible for adding them to the idle pool. It is safe to say that all UMS thread
// proxies loaned out to a UMS scheduler are back in the idle pool of the factory at the time the UMS scheduler/scheduler
// proxy (virtual processors roots and all) are destroyed, and the factory can safely be shutdown without worrying about
// stragglers.
m_pFactory->Reference();
m_id = ResourceManager::GetThreadProxyId();
m_hBlock = CreateEventW(NULL, FALSE, FALSE, NULL);
if (m_hBlock == NULL)
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
m_hPhysicalContext = LoadLibraryAndCreateThread(NULL,
m_stackSize*KB,
ThreadProxyMain,
this,
STACK_SIZE_PARAM_IS_A_RESERVATION,
&m_threadId);
}
/// <summary>
/// Destroys a thread proxy.
/// </summary>
ThreadProxy::~ThreadProxy()
{
CloseHandle(m_hBlock);
CloseHandle(m_hPhysicalContext);
m_pFactory->Release();
}
/// <summary>
/// Returns a process unique identifier for the thread proxy.
/// </summary>
unsigned int ThreadProxy::GetId() const
{
return m_id;
}
/// <summary>
/// Sets the priority of the underlying thread.
/// </summary>
/// <param name="priority">
/// The new priority value for the thread.
/// </param>
void ThreadProxy::SetPriority(int priority)
{
m_threadPriority = priority;
if (SetThreadPriority(m_hPhysicalContext, m_threadPriority) == 0)
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
/// <summary>
/// Blocks the thread proxy until is is resumed via ResumeExecution or a different thread proxy switching to it.
/// </summary>
void ThreadProxy::SuspendExecution()
{
ASSERT(m_fBlocked == FALSE);
InterlockedExchange(&m_fBlocked, TRUE);
WaitForSingleObject(m_hBlock, INFINITE);
ASSERT(m_fBlocked == TRUE);
InterlockedExchange(&m_fBlocked, FALSE);
}
/// <summary>
/// Resumes execution of a thread proxy.
/// </summary>
void ThreadProxy::ResumeExecution()
{
SetEvent(m_hBlock);
}
/// <summary>
/// Cancels the thread proxy causing the underlying thread to exit.
/// </summary>
void ThreadProxy::Cancel()
{
ASSERT(m_fCanceled == false);
m_fCanceled = true;
ResumeExecution();
}
/// <summary>
/// Spins until the 'this' thread proxy is in a firmly blocked state.
/// </summary>
/// <remarks>
/// This implements a sort of barrier. At certain points during execution, it is essential to wait until a thread proxy
/// has set the flag inidicating it is blocked, in order to preserve correct behavior. One example is if there is a race
/// between block and unblock for the same proxy, i.e. if a thread proxy is trying to block at the same time a different
/// context is trying to unblock it.
/// </remarks>
void ThreadProxy::SpinUntilBlocked()
{
if (m_fBlocked == FALSE)
{
_SpinWaitBackoffNone spinWait(_Sleep0);
do
{
spinWait._SpinOnce();
} while (m_fBlocked == FALSE);
}
ASSERT(m_fBlocked == TRUE);
}
/// <summary>
/// Thread start routine for proxies.
/// </summary>
DWORD CALLBACK ThreadProxy::ThreadProxyMain(LPVOID lpParameter)
{
ThreadProxy* pThreadProxy = reinterpret_cast<ThreadProxy*> (lpParameter);
// To start the dispatch loop cleanly, the context must block until it is switched to, or resumed..
WaitForSingleObject(pThreadProxy->m_hBlock, INFINITE);
InterlockedExchange(&pThreadProxy->m_fBlocked, FALSE);
pThreadProxy->Dispatch();
ASSERT(pThreadProxy->m_fCanceled);
// Thread proxy needs to be deleted after it is canceled and it returns from the dispatch loop.
delete pThreadProxy;
FreeLibraryAndDestroyThread(0);
return 0;
}
} // namespace details
} // namespace Concurrency