0

我尝试实现一个典型的线程应用程序,其中一个线程询问设备数据是否可用,将数据复制到自己的内存中,并向主线程发送数据可用的事件。主线程将数据复制到自己的内存中并显示在 GUI 上。

为此,我将 Visual Studio 2012 和 C++/CLI 与 Winforms 一起使用。

有一个类“Work”包含线程方法“checkDataIsAvailable”。“Work”类实现了一个带有委托“OnRetrievedData”的接口(而不是抽象类),该委托作为事件工作并在“Form1”中调用“BeginInvoke”以具有异步行为。还有一个方法“getData”,主线程可以从“checkDataIsAvailable”线程获取数据。此外,“Work”类尝试从“ValueGenerator”类中获取数据,该类可以代表任何真实设备。我为“工作”类中的“数据”确定了一个关键部分,称为“array^ m_Data;”。问题是“监视器”和“ReaderWriterLockSlim”工作方法都不起作用。使用“监视器”时,GUI 会延迟响应,并且缺少许多更新。使用“ReaderWriterLockSlim”应用程序崩溃。并且在不保存关键部分的情况下,应用程序可以正常工作。但我不知道原因,因为我确定必须保存数据。

我想简化源代码并强调重要的事情。

最重要的是线程方法:

System::Void Work::checkDataIsAvailable()
{
    while ( ( Thread::CurrentThread->ThreadState & Threading::ThreadState::Running) == Threading::ThreadState::Running )
    {
        m_WaitForDoCheckDataIsAvailableHandle->WaitOne();

        //Monitor::Enter(m_LockData);
        m_rwlock->EnterWriteLock();
        m_Data = m_ValueGenerator->getData();
        m_rwlock->ExitWriteLock();
        //Monitor::Exit(m_LockData);

        if ( nullptr != OnRetrievedData)
        {
            OnRetrievedData();
        }
    }
}

在这里您可以看到从“ValueGenerator”到变量 m_Data 的复制过程。在我看来,这是一个关键部分。然后将发送事件“OnRetrievedData”,表明数据可用。

此事件将获得 Form1:

System::Void Form1::OnAcquisitionUpdate()
{
    if(this->InvokeRequired == true)
    {
        OnAcquisitionUpdateDelegate^ onAcquisitionUpdateDelegate = gcnew OnAcquisitionUpdateDelegate(this, &Form1::OnAcquisitionUpdate);

        this->BeginInvoke(onAcquisitionUpdateDelegate);
        //this->Invoke(onAcquisitionUpdateDelegate);
    }
    else
    {
        if ( nullptr != m_Work )
        {
            //Thread::Sleep(5000); 

            array<System::Int32>^ data;

            m_Work->getData(data);

            dataResult_label->Text = data->Length.ToString();
        }
    }

}

“Form1::OnAcquisitionUpdate”通过“BeginInvoke”将其更改为主线程并再次调用“Form1::OnAcquisitionUpdate”,但现在“InvokeRequired”为假,因此调用“Work”类从主线程获取数据。

System::Void Work::getData(array<System::Int32>^% data)
{
    //Monitor::Enter(m_LockData);
    m_rwlock->EnterReadLock();
    Console::WriteLine(" getData() -> Data length = {0}", m_Data->Length);
    data = m_Data;
    m_rwlock->EnterReadLock();
    //Monitor::Exit(m_LockData);
}

在这里,我看到下一个关键部分,将为调用者 Form1 复制数据。

如果有人可以在这种情况下提供帮助,那就太好了。

4

1 回答 1

0

我找到了一个带有布尔标志的解决方案。该标志称为“System::Boolean m_bCanWriteData;” 并将以这种方式在“Work::checkDataIsAvailable()”和“Work::getData(...)”中使用。

System::Void Work::checkDataIsAvailable()
{
    while ( ( Thread::CurrentThread->ThreadState & Threading::ThreadState::Running) == Threading::ThreadState::Running )
    {
        m_WaitForDoCheckDataIsAvailableHandle->WaitOne();

#ifdef Use_Monitor
        Monitor::Enter(m_LockData);
#endif
#ifdef Use_RW_Lock
        m_rwlock->EnterWriteLock();
#endif
        if ( m_bCanWriteData )
        {
            m_bCanWriteData = false;

            m_Data = m_ValueGenerator->getData();
            debugOutput("Work::checkDataIsAvailable() -> Data length = " + m_Data->Length);

            if ( nullptr != OnRetrievedData)
            {
                OnRetrievedData();
            }
        }


#ifdef Use_RW_Lock
        m_rwlock->ExitWriteLock();
#endif

#ifdef Use_Monitor
        Monitor::Exit(m_LockData);
#endif

    }
}

System::Void Work::getData(array<System::Int32>^% data)
{
    debugOutput("Work::getData() START");
#ifdef Use_Monitor
    Monitor::Enter(m_LockData);
    debugOutput("Work::getData() + Monitor::Enter");
#endif
#ifdef Use_RW_Lock
        m_rwlock->EnterReadLock();
#endif
    m_WaitForDoCheckDataIsAvailableHandle->Reset(); // Block the thread method.
    debugOutput("Work::getData() -> Data length = " + m_Data->Length);
    data = m_Data;
    m_bCanWriteData = true;
    if ( m_bIsRunning ) 
    {
        m_WaitForDoCheckDataIsAvailableHandle->Set(); // Start the thread method.
    }

#ifdef Use_RW_Lock
        m_rwlock->EnterReadLock();
#endif
#ifdef Use_Monitor
        Monitor::Exit(m_LockData);
#endif
    debugOutput("Work::getData() END");
}

如您所见,写入和读出是受控的,它适用于监视器。

于 2013-04-29T20:09:31.360 回答