问题描述
这是我的线程类的骨架:
class MyThread {
public:
virutal 〜MyThread();
//将以svc()作为线程入口点开始线程
void start()= 0;
//派生类将专门讨论线程应该做什么
virtual void svc()= 0;
};
在代码中我创建了一个 MyThread
后来我想摧毁它。
在这种情况下,调用 MyThread〜MyThread()
。 MyThread:svc()
仍在运行并使用对象的数据成员。所以我需要一个礼貌地通知 MyThread:svc()
停止旋转,然后继续析构函数。
什么是可接受的方式来销毁线程对象?
注意:我在寻找平台无关解决方案。
UPD:很明显,问题的根源是C ++对象代表线程和OS线程之间没有关系。所以问题是:在对象破坏的上下文中,是否有一种可以接受的方式使线程对象像一个普通的C ++对象,或者应该被视为一个不寻常的对象(例如我们应该在调用之前调用join()?
考虑你的额外要求发表为Checkers的回复(这是
最直接的方式) / p>
我同意加入DTor由于各种原因是有问题的,但是从那时起,线程对象的生命周期与OS线程对象的生命周期无关。
首先,您需要将线程使用的数据与线程对象本身分开。 它们是具有不同生命周期要求的不同实体。
一种方法是使数据引用计数,并且有任何线程想要访问它,这样,没有线程会突然陷入空洞,但是一旦没有人接触到它,数据就会被破坏。
其次,关于线程加入时被销毁的线程对象:
我不知道这是否是个好主意。线程对象通常是一种查询线程状态的方法 - 但是线程对象一旦线程完成就会死掉,没有人能告诉你线程完成了。
一般来说,我将线程对象的生命周期与操作系统线程的生命周期完全解耦:销毁线程对象不应该影响线程本身。我看到两个基本的方法:
- 线程句柄对象 - 线程创建器返回的引用被重新计算,喜欢不影响操作系统线程。它将暴露诸如
Join
,IsFinished
等方法,并且可以访问线程共享数据。
(如果线程对象保存相关执行状态,threafFunc本身可以保存对它的引用,从而确保实例不会在线程结束之前释放)
- Thin Wrapper - 只需在操作系统线程句柄周围创建一个临时文件。你不能容易地为线程保持额外的状态,但它可能只是使它工作:在任何地方,你可以将一个操作系统线程句柄转换为线程对象。大多数通信 - 例如。告诉线程终止 - 将通过共享数据。
对于您的代码示例,将 start()
与 svc()
你大致使用这个API(XxxxPtr可以是例如boost :: shared_ptr):
{
public:
bool IsFinished();
void Join();
bool TryJoin(long timeout);
WorkerPtr GetWorker();
static ThreadPtr Start(WorkerPtr worker); //创建线程
};
class Worker
{
private:
virtual void Svc()= 0;
好友类Thread; //所以线程可以运行Svc()
}
Worker可以包含ThreadPtr本身,从而保证在执行 Svc()
期间线程对象存在。如果允许多个线程在相同的数据上工作,这将是一个线程列表。否则, Thread :: Start
将不得不拒绝已经与线程相关联的Workers。
动机:阻止的流氓线程的处理方式?
假设线程未能在时间内终止因为某种原因,即使你告诉它。您只有三个选择:
- 死锁,您的应用程序挂起。如果你加入析构函数,通常就会出现这种情况。
- 暴力终止线程。
- 让线程运行以完成自己的数据 - 您可以通知用户,他们可以安全地 保存&出口。或者你只是让流氓线程跳舞它自己的数据副本(不再由主线程引用),直到它完成。
Here is a skeleton of my thread class:
class MyThread {
public:
virutal ~MyThread();
// will start thread with svc() as thread entry point
void start() = 0;
// derive class will specialize what the thread should do
virtual void svc() = 0;
};
Somewhere in code I create an instance of MyThread
and later I want to destroy it.In this case MyThread~MyThread()
is called. MyThread:svc()
is still running and using the object's data members. So I need a way politely inform MyThread:svc()
to stop spinning, before proceeding with the destructor.
What is the acceptable way to destroy the thread object?
Note: I'm looking for platform agnostic solution.
UPD: It's clear that the root of problem is that there's no relationship between C++ object representing thread and OS thread. So the question is: in context of object destuction, is there an acceptable way to make thread object behave like an ordinary C++ object or should it be treated as an unusual one (e.g. should we call join() before destoying it?
Considering your additional requirements posted as comment to Checkers' reply (which is themost straightforward way to do that):
I agree that join in DTor is problematic for various reasons. But from that the lifetime of your thread object is unrelated to the lifetime of the OS thread object.
First, you need to separate the data the thread uses from the thread object itself. They are distinct entities with distinct lifetime requirements.
One approach is to make the data refcounted, and have any thread that wants to access it hold a strong reference to the data. This way, no thread will suddenly grab into the void, but the data will be destroyed as soon as noone touches it anymore.
Second, about the thread object being destroyed when the thread joins:
I am not sure if this is a good idea. The thread object is normally a way to query the state of a thread - but with a thread object that dies as soon as the thread finishes, noone can tell you wether the thread finished.
Generally, I'd completely decouple the lifetime of the thread object from the lifetime of the OS thread: Destroying your thread object should not affect the thread itself. I see two basic approaches to this:
- Thread Handle Object - reference counted again, returned by thread creator, can be released as early as one likes without affecting the OS thread. It would expose methods such as
Join
,IsFinished
, and can give access to the thread shared data.
(If the thread object holds relevant execution state, the threafFunc itself could hold a reference to it, thereby ensuring the instance won't be released before the thread ends)
- Thin Wrapper - You simply create a temporary around an OS thread handle. You could not hold additional state for the thread easily, but it might be just enough to make it work: At any place, you can turn an OS thread handle into an thread object. The majority of communication - e.g. telling the thread to terminate - would be via the shared data.
For your code example, this means: separate the start()
from the svc()
You'd roughly work with this API (XxxxPtr could be e.g. boost::shared_ptr):
class Thread
{
public:
bool IsFinished();
void Join();
bool TryJoin(long timeout);
WorkerPtr GetWorker();
static ThreadPtr Start(WorkerPtr worker); // creates the thread
};
class Worker
{
private:
virtual void Svc() = 0;
friend class Thread; // so thread can run Svc()
}
Worker could contain a ThreadPtr itself, giving you a guarantee that the thread object exists during execution of Svc()
. If multiple threads are allowed to work on the same data, this would have to be a thread list. Otherwise, Thread::Start
would have to reject Workers that are already associated with a thread.
Motivation: What to do with rogue threads that block?
Assuming a thread fails to terminate within time for one reason or another, even though you told it to. You simply have three choices:
- Deadlock, your applicaiton hangs. That usually happens if you join in the destructor.
- Violently terminate the thread. That's potentially a violent termination of the app.
- Let the thread run to completion on it's own data - you can notify the user, who can safely save & exit. Or you simply let the rogue thread dance on it's own copy of the data (not reference by the main thread anymore) until it completes.
这篇关于一种破坏“线程”的方法,类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!