2016-08-24 4 views
3

Я использую VS2015 и сталкиваюсь с чрезвычайно странной проблемой при использовании std :: thread.std :: thread throws Исключение нарушения прав доступа при создании с помощью аргументов?

void Klass::myfunc(int a, int b) { std::cout << a << ' ' << b << std::endl; } 
// ... 
auto t = std::thread(&Klass::myfunc, this, 100, 200); <- runtime error after called 
// ... 
t.join(); 

Он хорошо работает в режиме отладки, но при переключении в режим деблокирования генерирует «исключение нарушения доступа».

Более того, если я пытаюсь изменить "MyFunc" к этому:

void Klass::myfunc() { std::cout << "foo" << std::endl; } 
// ... 
auto t = std::thread(&Klass::myfunc, this); // everything goes well 
// ... 
t.join(); 

он хорошо работает снова.

Я гарантирую, что «& Klass :: myfunc» и «это» указатели не являются NULL. И после того, как вызывается ctor, происходит «соединение» после нескольких строк.

Я предполагаю, что это может быть какое-то «неопределенное поведение», но я понятия не имею, что это такое.

Стек вызовов что-то вроде этого:

000000c83a4ffd40() Unknown 
> distributed_word_embedding.exe!std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl multiverso::Communicator::*)(void) __ptr64,multiverso::Communicator * __ptr64>,std::default_delete<std::tuple<void (__cdecl multiverso::Communicator::*)(void) __ptr64,multiverso::Communicator * __ptr64> > > >::_Run(std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl multiverso::Communicator::*)(void),multiverso::Communicator *>,std::default_delete<std::tuple<void (__cdecl multiverso::Communicator::*)(void),multiverso::Communicator *> > > > * _Ln) Line 247 C++ 
    distributed_word_embedding.exe!std::_Pad::_Call_func(void * _Data) Line 210 C++ 
    ucrtbase.dll!00007ffabdc7be1d() Unknown 
    kernel32.dll!00007ffabfae8102() Unknown 
    ntdll.dll!00007ffac26bc5b4() Unknown 
+6

Что происходит после того, как вы создали нить? Вы «присоединяетесь» к этому? – doctorlove

+3

Вероятно, это намек на то, что это похоже на пожизненную проблему, где поток выдает экземпляр Klass и, таким образом, обманывает этот указатель. Присоединившись в нужном месте, вы можете предотвратить это. Однако мы не можем быть уверены, основываясь на представленном контенсе. – stefaanv

+0

@doctorlove stefaanv привет, спасибо за ваш ответ. На самом деле отладчик и журнал показывают, что программа выключена сразу после вызова ctor std :: thread, а «join» - несколько строк после. Я думаю, что проблема не в том, чтобы «присоединиться».И я также упомянул в вопросе, что если я называю «myfunc» без аргументов, все будет хорошо. – Wizmann

ответ

1

Вы всегда должны убедиться, что вы присоединитесь (или, возможно, снять) в thread, в противном случае оставляя main особенно с резьбой с помощью объектов (в данном случае this) будет (иногда) вызывают проблемы.

//... details omitted... 

int main() 
{ 
    auto t = std::thread(&Klass::myfunc, this); 
    t.join(); //<----- NOTE THIS 
} 

потоковая Энтони Уильяма blog проходит через это в деталях. С примером очень похож на ваш второй:

void my_thread_func() 
{ 
    std::cout<<"hello"<<std::endl; 
} 

int main() 
{ 
    std::thread t(my_thread_func); 
} 

Он говорит

Если скомпилировать и запустить это маленькое приложение, что происходит? Это принт привет, как мы хотели? Ну, на самом деле ничего не сказано. Это может быть . Я запускал это простое приложение несколько раз на моей машине, и выход был ненадежным: иногда он выводит «привет», с новой строкой; иногда он выводит «привет» без строки новой строки , а иногда ничего не выводит. Что с этим? Неужели такое простое приложение должно вести себя предсказуемо?

Затем он вводит идею использования join, как я сделал выше, и говорит,

Проблема заключается в том, что мы не ждем наш поток, чтобы закончить. Когда выполнение достигает конца main(), программа завершается, независимо от того, что делают другие потоки.