2012-07-31 136 views
2

如何能在一个这个简单的例子保证有序多线程输出

a->push_back(i) 

发生在这些线程启动的顺序?所以内容将是{1,2,3}。

#include <vector> 
#include <thread> 

void do_stuf(int i,std::vector<int> * a) 
{ 
    //do very long stuff 
    a->push_back(i); 
} 


int main() 
{ 
    std::vector<int> tmp; 
    std::thread t1(do_stuf,1,&tmp); 
    std::thread t2(do_stuf,2,&tmp); 
    std::thread t3(do_stuf,3,&tmp); 

    t1.join(); 
    t2.join(); 
    t3.join(); 
} 
+10

不要使用线程? – Mysticial 2012-07-31 18:38:42

+0

在一个很长的词里:不。 – 2012-07-31 18:39:39

+1

此外,这是违法的同时修改'的std :: VECTOR'这样。 – Mysticial 2012-07-31 18:41:40

回答

5

一种方式是一个指针或引用传递线程你希望他们来存储他们的结果(并确保它仍然分配给线程的地方寿命),像这样:

void do_stuf(int i, int* a) 
{ 
    //do very long stuff 
    *a = i; 
} 


int main() 
{ 
    std::vector<int> tmp(3); 
    std::thread t1(do_stuf,1,&tmp[0]); 
    std::thread t2(do_stuf,2,&tmp[1]); 
    std::thread t3(do_stuf,3,&tmp[2]); 

    t1.join(); 
    t2.join(); 
    t3.join(); 
} 

它不是从你的例子你正在努力实现明确的,但你必须看看的std ::承诺和std ::未来,想通了,他们做了什么?他们可能是你想要的。

(在这种情况下,vector :: push_back的问题在于它不是线程安全的,如果两个push_backs执行重叠,它可能覆盖相同的元素,或者可能重新分配移动所有存储位置的数组元素)。

+0

std :: future和std :: promise看起来很有希望。试图将它们添加到“简单示例”中,并根据需要完成所有操作。谢谢 :) – UldisK 2012-07-31 19:40:39

-1

当线程长时间处理完成后,您可以有一个“同步”推回的回调。

侧面说明,如果你是在一个多线程的世界想要的push_back我会用列表,而不是载体,名单将保持比被删除的元素之外的一切迭代器......

+0

std :: list与std :: vector一样是线程安全的;也就是说,它不是线程安全的。 – DanielKO 2012-08-01 19:42:53

+0

我从来没有说std :: list是线程安全的,但比较每个迭代器将被无效与容器上的任何行动的std :: vector,以std :: list是亵渎神灵。推到列表是好的,推到矢量不是。这是我的观点。 – clanmjc 2012-08-01 21:17:06

+0

你的评论说“多线程世界中的push_back”,无论多线程如何,迭代器的失效都适用。这是一个解决不同问题的评论;这没有错,但这不是问题。 – DanielKO 2012-08-01 22:18:28

0

你可能接近此一对夫妇的方式。如果您不关心他们完成的顺序,只是结果顺序,请使用任务ID及其结果推送对象,然后按任务ID对对象进行排序,并在完成所有对象时进行打印。

如果您确实希望它们在上一个任务完成前不报告,则需要等待上一个任务。

-1

正如所写,插入可以任意排序。为了确保您按照您的要求订购,您基本上需要一个售票系统,在该系统中,线程将进入睡眠/旋转状态,直到请求其售票号在高层次上,这意味着您向每个线程(它的票号)传递额外的信息,拥有一个全局共享值,它是请求的票证ID,并且线程代码具有确保不执行插入的一些代码直到全球ID等于本地号码。

+0

由于编写的程序具有未定义的行为,因为多个线程可能会尝试写入非线程安全的容器,如std :: vector。此外,轮询全局共享值的建议效率非常低,并且错误,因为不能保证线程将看到更新的值,并且在值更新到下一张票之前可能会错过其插槽。 – DanielKO 2012-07-31 19:18:54

+0

你是对的,我没有清楚地描述我提出的解决方案。这个想法是,只有当相应的线程执行了它的动作(并且显然是用互斥和条件变量保护的),全局标签才会被更新。 – 2012-08-01 04:15:47

0

对不起,我错过了“//做很长的东西”。如果在长动作结束时有一个特定的动作需要按照特定的顺序完成,那么你可以简单地将这些代码放在3个线程的连接之后。

如果这个动作必须通过特定的线程来完成,你可以使用一个互斥体和条件变量保护的共享计数器的信号,这个计数器已经改变。更改计数器时,必须通知所有服务员,因为需要唤醒特定的线程。在函数结束时,函数等待计数器具有线程特定的值。第一个线程正在等待初始值,做它的动作,递增计数器。第二个线程正在等待计数器成为第二个值等