我正在构建自己的网络服务器,我希望它是多线程的。我不知道如何启动新线程,将其存储在某个列表中,检测线程何时完成并从等待队列添加一些新线程。谁能给我一个简单的例子如何做到这一点?现在我以这种方式运行线程:
boost::thread t(app, client_fd); // but it's not good way because I can't control it
下面是pseucode,它说明了我的意思:
class worker
{
public:
void run(int cfd)
{
// do something
}
}
std::vector<int> waitQueue;
std::vector<worker> runningQueue;
onAcceptClient(int client_fd)
{
waitQueue.insert(client_fd);
}
while(1) // this must run in single thread
{
client_fd = accept(...);
onAcceptClient(client_fd);
}
while(1) // this must run in single thread
{
if (runningQueue.size() < 128)
{
int diff = 128 - runningQueue.size() ;
for (int a = 0; a < diff; a++)
{
int cfc = waitQueue.pop();
worker w;
w.run(cfc);
runningQueue.insert(w);
}
}
}
答案 0 :(得分:0)
这是我实现这个的方法。
首先,我有一个基本的ThreadWorker类,我的实际处理任务可以来自。请注意,这是特定于Windows的,但您可以将boost mutex替换为CriticalSection,并且信号量的POSIX实现位于:http://linux.die.net/man/7/sem_overview
class ThreadWorker
{
public:
ThreadWorker(void)
{
signalfinished = NULL;
forcesignal = false;
}
virtual ~ThreadWorker(void)
{
if (signalfinished!=NULL) ReleaseSemaphore(signalfinished,1,NULL);
}
DWORD ThreadId;
HANDLE threadhandle;
HANDLE signalfinished;
bool forcesignal;
static DWORD WINAPI workerthread(void *param)
{
ThreadWorker *worker = (ThreadWorker*)param;
worker->signalfinished = CreateSemaphore(NULL,0,1,NULL);
worker->RunTask();
ReleaseSemaphore(worker->signalfinished,1,NULL);
}
void StartWorker()
{
CreateThread(NULL,NULL,ThreadWorker::workerthread,this,0,&ThreadId);
}
void WaitUntilWorkerFinished()
{
DWORD waitresult;
do
{
waitresult = WaitForSingleObject(signalfinished,1000);
} while (waitresult!=WAIT_OBJECT_0 && !forcesignal);
}
virtual void RunTask()=0;
};
然后,我有一个ThreadManager来管理ThreadWorker对象的队列。同样,您可以将互斥锁替换为CriticalSection。我更喜欢std :: list到std :: vector,因为vector是连续的。
class ThreadManager
{
CRITICAL_SECTION critsec;
std::list<ThreadWorker*> taskqueue;
public:
ThreadManager(void)
{
InitializeCriticalSection(&critsec);
}
void AddTaskToQueue(ThreadWorker *task)
{
EnterCriticalSection(&critsec);
taskqueue.push_back(task);
LeaveCriticalSection(&critsec);
}
void ProcessTaskQueue()
{
while (true)
{
EnterCriticalSection(&critsec);
ThreadWorker *thistask = taskqueue.front();
taskqueue.pop_front();
LeaveCriticalSection(&critsec);
thistask->StartWorker();
}
}
~ThreadManager(void)
{
DeleteCriticalSection(&critsec);
}
};
要将任务添加到队列,我们需要一个实现ThreadWorker的子类。
class SomeWorkerTask : public ThreadWorker
{
public:
SomeWorkerTask(void);
virtual ~SomeWorkerTask(void);
void RunTask()
{
std::cout << "Hello, I am a worker task runing on thread id " << ThreadId << std::endl;
}
};
创建SomeWorkerTask的新实例并将其添加到队列中,然后处理队列。在你的情况下,你将有不同的线程向队列添加任务和处理队列,但我认为你会得到这个想法。
SomeWorkerTask *atask = new SomeWorkerTask();
ThreadManager manager;
manager.AddTaskToQueue(atask);
manager.ProcessTaskQueue();
如果您想知道任务何时完成处理,您可以从另一个线程调用ThreadWorker :: WaitUntilWorkerFinished或将调用添加到ProcessTaskQueue。您可以修改ThreadManager,以便拥有一个等待任务队列,一个正在运行的任务队列和一个已完成任务的第三个队列。将任务从等待队列中弹出后,将其添加到正在运行的队列中,并使用任务的信号量确定它何时完成,然后将其添加到已完成的任务/将其从正在运行的任务中删除。请注意,标准容器(如矢量,地图和列表)不是线程安全的,因此您应始终围绕使用互斥锁(例如临界区或互斥锁)插入/移除容器的操作。
希望有所帮助。
答案 1 :(得分:0)
你需要一个从多个线程同时访问的队列的互斥锁,并且由于你正在测试runningQueue.size() < 128
,你还需要一个条件变量。如果您的编译器支持c ++ 11,std::mutex
和std::condition_variable
将完成工作,或者boost::mutex
和boost::condition_variable
都可以。
所以它有点像这样:
onAcceptClient(int client_fd)
{
boost::mutex::scoped_lock waitLock(mWaitMutex);
waitQueue.insert(client_fd);
}
while(1) // this must run in single thread
{
boost::mutex::scoped_lock lock(mRunningMutex);
// wait if the runningQueue.size >= 128
while(runningQueue.size() >= 128)
mRunningCond.wait(lock);
if (runningQueue.size() < 128)
{
int diff = 128 - runningQueue.size() ;
for (int a = 0; a < diff; a++)
{
boost::mutex::scoped_lock waitLock(mWaitMutex);
int cfc = waitQueue.pop();
worker w;
w.run(cfc);
runningQueue.insert(w);
}
}
}