Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/286.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何使Win32/MFC线程在lockstep中循环?_C++_Multithreading_Mfc - Fatal编程技术网

C++ 如何使Win32/MFC线程在lockstep中循环?

C++ 如何使Win32/MFC线程在lockstep中循环?,c++,multithreading,mfc,C++,Multithreading,Mfc,我不熟悉Windows中的多线程,所以这可能是一个很小的问题:确保线程在lockstep中执行循环的最简单方法是什么 我尝试将事件的共享数组传递给所有线程,并在循环结束时使用WaitForMultipleObjects对它们进行同步,但这会在一个(有时是两个)周期后导致死锁。下面是我当前代码的简化版本(只有两个线程,但我想使其具有可伸缩性): typedef结构 { 整数秩; 处理*step_事件; }迭代参数; int main(int argc,字符**argv) { // ... 迭代参数

我不熟悉Windows中的多线程,所以这可能是一个很小的问题:确保线程在lockstep中执行循环的最简单方法是什么

我尝试将
事件的共享数组
传递给所有线程,并在循环结束时使用
WaitForMultipleObjects
对它们进行同步,但这会在一个(有时是两个)周期后导致死锁。下面是我当前代码的简化版本(只有两个线程,但我想使其具有可伸缩性):

typedef结构
{
整数秩;
处理*step_事件;
}迭代参数;
int main(int argc,字符**argv)
{
// ...
迭代参数p[2];
处理步骤_事件[2];
for(int j=0;jstep_事件,真,无限);
}
else if(秩==1)
{
//做点别的
SetEvent(p->step_事件[1]);
WaitForMultipleObjects(2,p->step_事件,TRUE,无限);
}
}
返回0;
}
< P>(我知道我正在混合C和C++,实际上是我试图并行化的遗留C代码) 阅读MSDN的文档,我认为这应该是可行的。但是,线程0只打印一次,偶尔打印两次,然后程序挂起。这是同步线程的正确方法吗?如果没有,您会推荐什么(MFC中是否真的没有内置的屏障支持?)


编辑:此解决方案是错误的,甚至包括。例如,考虑这种情况:

  • 线程0设置其事件并调用Wait,blocks
  • 线程1设置其事件并调用Wait,blocks
  • 线程0从等待返回,重置其事件,并在线程1未获得控制的情况下完成一个周期
  • 线程0设置自己的事件并调用Wait。由于线程1还没有机会重置其事件,因此线程0的等待将立即返回,线程将失去同步

  • 因此问题仍然存在:如何安全地确保线程保持锁定状态?

    要使其工作,请将
    CreateEvent
    的第二个参数设置为
    TRUE
    。这将使事件“手动复位”,并防止
    Waitxxx
    复位。 然后在循环开始处放置一个
    ResetEvent

    我通过谷歌搜索“barrier synchronization windows”找到了这个(下载SyncTools.zip)。它使用一个CriticalSection和一个事件为N个线程实现一个屏障。

    Introduction

    我为您设计了一个简单的C++程序(在Visual Studio 2010中进行了测试)。它只使用Win32 API(以及用于控制台输出的标准库和一些随机化)。您应该能够将它放入一个新的Win32控制台项目(没有预编译头),编译并运行


    解决方案 请注意,为了简单起见,每个线程都有随机的迭代持续时间,但该线程的所有迭代都将使用相同的随机持续时间(即,它在迭代之间不会改变)


    它是如何工作的?
    解决方案的“核心”是“RandezvousOthers”函数。此函数将阻塞共享信号量(如果调用此函数的线程不是最后一个调用此函数的线程),或者重置同步结构并取消阻塞共享信号量上的所有线程(如果调用此函数的线程是最后一个调用此函数的线程).

    我不想让
    等待…
    重置事件吗?否则,线程在第一个周期后不会同步,否?否,因为返回的第一次等待会重置事件,阻止另一次等待返回。正确。顺便说一句,
    Wait
    可以代表后代在循环结束时组合,而不是在if块中:-),感谢您发布了完整的示例。我实现了相同的逻辑(共享计数器和信号量),它似乎工作得很好。
    typedef struct
    {
        int rank;
        HANDLE* step_events;
    } IterationParams;
    
    int main(int argc, char **argv)
    {
        // ...
    
        IterationParams p[2];
        HANDLE step_events[2];
        for (int j=0; j<2; ++j)
        {
            step_events[j] = CreateEvent(NULL, FALSE, FALSE, NULL);
        }
    
        for (int j=0; j<2; ++j)
        {
            p[j].rank = j;
            p[j].step_events = step_events;
            AfxBeginThread(Iteration, p+j);
        }
    
        // ...
    }
    
    UINT Iteration(LPVOID pParam)
    {
        IterationParams* p = (IterationParams*)pParam;
        int rank = p->rank;
    
        for (int i=0; i<100; i++)
        {
            if (rank == 0)
            {
                printf("%dth iteration\n",i);
                // do something
                SetEvent(p->step_events[0]);
                WaitForMultipleObjects(2, p->step_events, TRUE, INFINITE);
            }
            else if (rank == 1)
            {
                // do something else
                SetEvent(p->step_events[1]);
                WaitForMultipleObjects(2, p->step_events, TRUE, INFINITE);
            }
        }
        return 0;
    }
    
    #include <tchar.h>
    #include <windows.h>
    
    
    //---------------------------------------------------------
    // Defines synchronization info structure. All threads will
    // use the same instance of this struct to implement randezvous/
    // barrier synchronization pattern.
    struct SyncInfo
    {
        SyncInfo(int threadsCount) : Awaiting(threadsCount), ThreadsCount(threadsCount), Semaphore(::CreateSemaphore(0, 0, 1024, 0)) {};
        ~SyncInfo() { ::CloseHandle(this->Semaphore); }
        volatile unsigned int Awaiting; // how many threads still have to complete their iteration
        const int ThreadsCount;
        const HANDLE Semaphore;
    };
    
    
    //---------------------------------------------------------
    // Thread-specific parameters. Note that Sync is a reference
    // (i.e. all threads share the same SyncInfo instance).
    struct ThreadParams
    {
        ThreadParams(SyncInfo &sync, int ordinal, int delay) : Sync(sync), Ordinal(ordinal), Delay(delay) {};
        SyncInfo &Sync;
        const int Ordinal;
        const int Delay;
    };
    
    
    //---------------------------------------------------------
    // Called at the end of each itaration, it will "randezvous"
    // (meet) all the threads before returning (so that next
    // iteration can begin). In practical terms this function
    // will block until all the other threads finish their iteration.
    static void RandezvousOthers(SyncInfo &sync, int ordinal)
    {
        if (0 == ::InterlockedDecrement(&(sync.Awaiting))) { // are we the last ones to arrive?
            // at this point, all the other threads are blocking on the semaphore
            // so we can manipulate shared structures without having to worry
            // about conflicts
            sync.Awaiting = sync.ThreadsCount;
            wprintf(L"Thread %d is the last to arrive, releasing synchronization barrier\n", ordinal);
            wprintf(L"---~~~---\n");
    
            // let's release the other threads from their slumber
            // by using the semaphore
            ::ReleaseSemaphore(sync.Semaphore, sync.ThreadsCount - 1, 0); // "ThreadsCount - 1" because this last thread will not block on semaphore
        }
        else { // nope, there are other threads still working on the iteration so let's wait
            wprintf(L"Thread %d is waiting on synchronization barrier\n", ordinal);
            ::WaitForSingleObject(sync.Semaphore, INFINITE); // note that return value should be validated at this point ;)
        }
    }
    
    
    //---------------------------------------------------------
    // Define worker thread lifetime. It starts with retrieving
    // thread-specific parameters, then loops through 5 iterations
    // (randezvous-ing with other threads at the end of each),
    // and then finishes (the thread can then be joined).
    static DWORD WINAPI ThreadProc(void *p)
    {
        ThreadParams *params = static_cast<ThreadParams *>(p);
        wprintf(L"Starting thread %d\n", params->Ordinal);
    
        for (int i = 1; i <= 5; ++i) {
            wprintf(L"Thread %d is executing iteration #%d (%d delay)\n", params->Ordinal, i, params->Delay);
            ::Sleep(params->Delay); 
            wprintf(L"Thread %d is synchronizing end of iteration #%d\n", params->Ordinal, i);
            RandezvousOthers(params->Sync, params->Ordinal);
        }
    
        wprintf(L"Finishing thread %d\n", params->Ordinal);
        return 0;
    }
    
    
    //---------------------------------------------------------
    // Program to illustrate iteration-lockstep C++ solution.
    int _tmain(int argc, _TCHAR* argv[])
    {
        // prepare to run
        ::srand(::GetTickCount()); // pseudo-randomize random values :-)
        SyncInfo sync(4);
        ThreadParams p[] = {
            ThreadParams(sync, 1, ::rand() * 900 / RAND_MAX + 100), // a delay between 200 and 1000 milliseconds will simulate work that an iteration would do
            ThreadParams(sync, 2, ::rand() * 900 / RAND_MAX + 100),
            ThreadParams(sync, 3, ::rand() * 900 / RAND_MAX + 100),
            ThreadParams(sync, 4, ::rand() * 900 / RAND_MAX + 100),
        };
    
        // let the threads rip
        HANDLE t[] = {
            ::CreateThread(0, 0, ThreadProc, p + 0, 0, 0),
            ::CreateThread(0, 0, ThreadProc, p + 1, 0, 0),
            ::CreateThread(0, 0, ThreadProc, p + 2, 0, 0),
            ::CreateThread(0, 0, ThreadProc, p + 3, 0, 0),
        };
    
        // wait for the threads to finish (join)
        ::WaitForMultipleObjects(4, t, true, INFINITE);
    
        return 0;
    }
    
    Starting thread 1
    Starting thread 2
    Starting thread 4
    Thread 1 is executing iteration #1 (712 delay)
    Starting thread 3
    Thread 2 is executing iteration #1 (798 delay)
    Thread 4 is executing iteration #1 (477 delay)
    Thread 3 is executing iteration #1 (104 delay)
    Thread 3 is synchronizing end of iteration #1
    Thread 3 is waiting on synchronization barrier
    Thread 4 is synchronizing end of iteration #1
    Thread 4 is waiting on synchronization barrier
    Thread 1 is synchronizing end of iteration #1
    Thread 1 is waiting on synchronization barrier
    Thread 2 is synchronizing end of iteration #1
    Thread 2 is the last to arrive, releasing synchronization barrier
    ---~~~---
    Thread 2 is executing iteration #2 (798 delay)
    Thread 3 is executing iteration #2 (104 delay)
    Thread 1 is executing iteration #2 (712 delay)
    Thread 4 is executing iteration #2 (477 delay)
    Thread 3 is synchronizing end of iteration #2
    Thread 3 is waiting on synchronization barrier
    Thread 4 is synchronizing end of iteration #2
    Thread 4 is waiting on synchronization barrier
    Thread 1 is synchronizing end of iteration #2
    Thread 1 is waiting on synchronization barrier
    Thread 2 is synchronizing end of iteration #2
    Thread 2 is the last to arrive, releasing synchronization barrier
    ---~~~---
    Thread 4 is executing iteration #3 (477 delay)
    Thread 3 is executing iteration #3 (104 delay)
    Thread 1 is executing iteration #3 (712 delay)
    Thread 2 is executing iteration #3 (798 delay)
    Thread 3 is synchronizing end of iteration #3
    Thread 3 is waiting on synchronization barrier
    Thread 4 is synchronizing end of iteration #3
    Thread 4 is waiting on synchronization barrier
    Thread 1 is synchronizing end of iteration #3
    Thread 1 is waiting on synchronization barrier
    Thread 2 is synchronizing end of iteration #3
    Thread 2 is the last to arrive, releasing synchronization barrier
    ---~~~---
    Thread 2 is executing iteration #4 (798 delay)
    Thread 3 is executing iteration #4 (104 delay)
    Thread 1 is executing iteration #4 (712 delay)
    Thread 4 is executing iteration #4 (477 delay)
    Thread 3 is synchronizing end of iteration #4
    Thread 3 is waiting on synchronization barrier
    Thread 4 is synchronizing end of iteration #4
    Thread 4 is waiting on synchronization barrier
    Thread 1 is synchronizing end of iteration #4
    Thread 1 is waiting on synchronization barrier
    Thread 2 is synchronizing end of iteration #4
    Thread 2 is the last to arrive, releasing synchronization barrier
    ---~~~---
    Thread 3 is executing iteration #5 (104 delay)
    Thread 4 is executing iteration #5 (477 delay)
    Thread 1 is executing iteration #5 (712 delay)
    Thread 2 is executing iteration #5 (798 delay)
    Thread 3 is synchronizing end of iteration #5
    Thread 3 is waiting on synchronization barrier
    Thread 4 is synchronizing end of iteration #5
    Thread 4 is waiting on synchronization barrier
    Thread 1 is synchronizing end of iteration #5
    Thread 1 is waiting on synchronization barrier
    Thread 2 is synchronizing end of iteration #5
    Thread 2 is the last to arrive, releasing synchronization barrier
    ---~~~---
    Finishing thread 4
    Finishing thread 3
    Finishing thread 2
    Finishing thread 1