-1

关于 C++ 类的菜鸟问题。

我有可以多次调用的函数 A,每次调用它都会使用函数 B 创建一个新线程。函数 b 调用函数 C,该函数调用许多 Win32 API 进行大量处理,其中的数据都是函数 C 的本地数据。

问:将函数 C 移动到一个类中并从函数 B 中将其声明为 New 是否可以解决任何线程安全问题?换句话说,因为 fncC() 存在于程序内存地址中的同一地址,我担心如果多个函数同时调用它,程序会崩溃。

fncA()
{
_beginthreadex(...fncB)
}

fncB()
{
fncC()
}

fncC()
{
RegCreateKeyEx(...)
}
4

3 回答 3

0

它可能会也可能不会,取决于类的实际作用。

无论代码是作为独立函数实现还是作为类的方法实现,代码仍然在任何线程上下文调用它的情况下执行。将它放在一个类中可以将隐藏this参数引入方法调用中。如果多个线程同时使用同一个类实例,您仍然必须处理线程并发问题。另一方面,如果该方法只访问属于该类的数据,并且每个线程都在对该类的不同实例进行操作,那么这通常是安全的。当多个线程同时访问相同的数据/资源时,会出现并发问题。

于 2013-10-10T22:41:09.497 回答
0

如果FncC只使用本地数据,并调用线程安全的方法,那么FncC就是线程安全的。

如果FncC甚至调用单个线程不安全的方法,则FncC不是线程安全的。

如果FncC在没有适当保护的情况下访问超出方法范围的任何数据——例如静态数据、全局数据、类成员数据,没有正确使用互斥锁——那么FncC就不是线程安全的。

无耻地链接到我之前关于线程安全的答案: 如何使应用程序线程安全?

于 2013-10-10T22:42:02.363 回答
0

除非您的 C 类成员函数的实现使用自修改代码(不太可能),否则访问程序内存本身不会成为问题。您的问题将是访问共享的可变数据和资源。

如果我正确理解了这个问题,您是说您将创建一个类“C”的新实例,并从另一个线程专门调用 C 类的成员函数。

例如伪代码:

void thread1() {
   auto_ptr<C> pc(new C());
   while (1) {
      pc->f();
   }
}
void thread2() {
   auto_ptr<C> pc(new C());
   while (1) {
      pc->f();
   }
}

由于函数 C::f() 中的代码不会更改,因此并发执行它是安全的。

但是,如果 C::f() 中的代码访问任何共享数据,那么除非您进行线程同步,否则它是不安全的。

例如,如果 C::f() 看起来像这样并且 C 有一个成员变量 ' int i_' 那么这是安全的,前提是没有其他线程更改该值:

void C::f() {
    ++i_;
}

仅当没有其他线程读取或修改 C 的“i_”成员的该实例时,这才是安全的。在某些情况下,如果只有其他线程读取可能i_是安全的,但前提是您可以保证写入i_是原子的。还有更多关于指令顺序的警告以及您在程序中其他地方可能期望的内容,因此任何对此类内容的依赖都需要格外小心。在这个人为的例子中,没有其他线程会知道'C'的实例,所以它会很好。

如果 C::f() 看起来像这样:

void C::f() {
    static int i = 0;
    std::cout << i++ << std::endl;
}

那么它肯定是不安全的。静态 int 是共享数据 - 所有对 C::f 的调用都可以访问相同的数据,无论它是什么实例。' ' 的静态初始化程序存在数据竞争i(第一次将其设置为零,编译器将为其插入一些簿记数据)。此外, ' ' 的增量i可能不是原子操作——因此它是不安全的。

这里使用全局变量std::cout也不是线程安全的。即使输出流在内部实现了互斥锁,您也在这里执行了两个单独的操作,并且您可能会遇到竞争。如果输出流内部没有线程同步,那么您可能会有各种未定义的行为。

于 2013-10-10T22:57:31.337 回答