如何强制线程按严格的顺序工作? [英] How to force threads to work in strict order?

查看:121
本文介绍了如何强制线程按严格的顺序工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用WinAPI的关键部分作为同步机制编写一个多线程控制台应用程序。我需要创建5个线程,每个线程都有自己的字符串来显示线程。线程应该逐个顺序输出它们的字符串。



所以,我有线程函数:

  void thread_routine(void * data)
{
std :: string * st =(std :: string *)data;
while(1)
{
/ * enter critical section * /
std :: cout< st; //不完全是这样,但我希望你明白我的意思。
Sleep(100);
/ * leave critical section * /
}
}

在我的 main()

  for(int i = 0; i <10; ++ i)
_beginthread(thread_routine,0,strings [i]);好吧,我希望看到像 [o1] 这样的东西::

/ p>

  1)第一个线程
2)第二个线程
3)第三个线程
4)第四个线程
5)第五个线程
...等等

这个输出我看到像 [o2]

  1)第一个线程
1)第一个线程
3)第三个线程
2)第二个线程
5)第五个线程
...等等

我清楚地知道发生了什么:线程通过临界区的顺序是未知的,所以该区段将被随机捕获,需要同步我的线程以获得像 [o1] 的输出。那么我该怎么办呢?是否有任何模式?

解决方案

关键部分不是解决这个问题的方法。关键部分只是一个互斥设备。它旨在确保在给定时间只有一个线程可以执行特定的代码片段。它不是用于测序,也不是特别好的任务。问题是,你不能在没有获得它的情况下对关键部分等待



考虑一个情况,其中线程B必须等待线程A完成后才能继续其工作。如果你使用临界区(调用 cs1 ),你必须确保线程A在线程B尝试获取它之前获取它。这意味着你必须100%确保线程A开始执行,并在线程B可以获得关键部分之前获取关键部分。



事件对象 ,另一方面,允许您等待事件或如果事件已经发生则继续。因此线程B可以在线程A甚至开始之前等待事件。它将等待,直到线程A(或某人)设置事件。你描述的问题(线程B需要等待一些事件发生)是确切事件对象设计解决的事情的类型。



给定3个并行处理一段时间的线程,但在某些时候需要等待一些其他事件发生,你可以编写代码来创建并等待事件。简要:

  HANDLE event1_done = CreateEvent(...); 
HANDLE event2_done = CreateEvent(...);
HANDLE event3_done = CreateEvent(...);

所有活动都是手动重设,且不会显示其初始状态。



主线程启动三个事件。然后它等待第三个完成:

  WaitForSingleObject(event3_done,INFINITE); 

各个线程处理并等待各自的事件。线程1,当然不会等待。它只是这样做:

  //线程1 
//处理
//它完成
SetEvent(event1_done);

线程2进行处理,等待 event1_done ,然后设置 event2_done

  //线程2 
// do processing
//等待第一个线程完成
WaitForSingleObject(event1_done,INFINITE);
// do whatever
//然后通知它完成了
SetEvent(event2_done);

线程3就像线程2;只有事件名称已更改。



此方法与使用临界区之间的区别是多个线程可能正在等待同一个事件对象。当该事件设置时,等待该对象的所有线程全部被释放。



另外请注意,如果您想要重新使用这些活动,您可以使用自动重置活动。需要重置它们(调用 ResetEvent )。当你这样做取决于你。


I'm writing a multi-thread console application using WinAPI's critical sections as the synchronizing mechanism. I need to create 5 threads, every thread has its own string to be displayed by the thread. Threads should output their strings sequentially, one by one.

So, I have thread function:

void thread_routine(void* data)
{
    std::string* st = (std::string*)data;
    while(1)
    {
        /* enter critical section */
        std::cout << st; // not exactly that way, but I hope that you understood what I've meant.
        Sleep(100);
        /* leave critical section */
    }
}

And in my main()

for(int i = 0; i < 10; ++i)
    _beginthread(thread_routine, 0, strings[i]);

Well I've expected to see something like [o1]:

1) First thread
2) Second thread
3) Third thread
4) Fourth thread
5) Fifth thread
... and so on

But instead of this output I saw something like [o2]:

1) First thread
1) First thread
3) Third thread
2) Second thread
5) Fifth thread
... and so on

I clearly understand what happens: the order in which the threads passes through the critical section is unknown, so the section will be captured by a random one, but I need to synchronize my threads to get an output like [o1]. So what do I have to do? Are there any patterns?

解决方案

Critical sections are not the way to solve this problem. A critical section is nothing more than a mutual exclusion device. It's intended to ensure that only one thread can be executing a particular piece of code at a given time. It's not intended to be used for sequencing, nor is it particularly good for that task. The problem is that you can't wait on a critical section without acquiring it.

Consider a case in which Thread B has to wait for Thread A to finish before continuing with its work. If you use a critical section (call it cs1), then you have to ensure that Thread A acquires it before Thread B tries to acquire it. That means you have to make 100% sure that Thread A begins execution and acquires the critical section before Thread B can potentially acquire the critical section.

Event Objects, on the other hand, allow you to wait for an event or continue if the event has already occurred. So Thread B can wait on the event before Thread A even starts. And it will wait until Thread A (or somebody) sets the event. The problem you describe (thread B needing to wait for some event to happen) is exactly the type of thing that event objects were designed to solve.

Given 3 threads that process in parallel for some time, but at some point need to wait for some other event to happen, you'd write code to create and wait on the events. Briefly:

HANDLE event1_done = CreateEvent(...);
HANDLE event2_done = CreateEvent(...);
HANDLE event3_done = CreateEvent(...);

All events are manual reset, and their initial state is not signaled.

The main thread starts the three events. Then it waits on the third one to be finished:

WaitForSingleObject(event3_done, INFINITE);

The individual threads do their processing and wait on their respective events. Thread 1, of course, doesn't wait. It just does this:

// thread 1
// do processing
// then signal that it's finished
SetEvent(event1_done);

Thread 2 does its processing, waits on event1_done, and then sets event2_done:

// thread 2
// do processing
// wait for the first thread to complete
WaitForSingleObject(event1_done, INFINITE);
// do whatever
// and then signal that it's done
SetEvent(event2_done);

Thread 3 is just like Thread 2; only the event names have changed.

The difference between this method and using critical sections is that multiple threads could be waiting on the same event object. When that event is set, all of the threads waiting on that object are released. If you want only one of them released, then you'd use an auto reset event.

Also note that if you want to re-use those events, you'll need to reset them (call ResetEvent). When you do that is up to you.

这篇关于如何强制线程按严格的顺序工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆