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

#c #multithreading #threadpool

Вопрос:

Я учусь на первом курсе по разработке программного обеспечения, и пока это мой второй блок. так что полегче со мной, пожалуйста, ха-ха

Моя программа threadpool (код в основном был предоставлен мне из uni, а дополнения получены из code blacksmith на YT) выдает мне ошибку:

Исключение, выданное в 0x7b5476 43 (msvcp140d.dll) в файле «имя_проекта».exe: 0xC0000005: Место чтения с нарушением доступа 0x00000014.

Мой учитель говорит мне: «Эта ошибка вызова мьютекса означает, что вы неправильно используете мьютекс», но мне кажется, что я использую их правильно.

Я прочитал здесь по другому вопросу, что «Местоположение для чтения с нарушением доступа 0x000000whatever» означает, что вы разыменовываете указатель, который не был инициализирован и, следовательно, имеет значения мусора.

Но я, кажется, не могу найти источник этого разыменованного указателя.. Не мог бы кто-нибудь, пожалуйста, взглянуть на мой код и сказать мне, что происходит?? насколько я могу судить, это должно сработать.

Примечание: он действительно строится, но не компилируется, и я все еще работаю над ним, так что может быть тот же мусор #inlcudes или использование std::’s.

Пул потоков.h

  
#pragma once
#include <thread>
#include <vector>
#include "CTask.h"
#include "WorkQueue.h"

using std::thread;
using std::vector;
using std::mutex;
using std::ref;

extern unsigned int h_ThreadCapacity;

class Threadpool
{
public:
    ~Threadpool()
    {
        Stop();
    }; 

    void Initialize()
    {
        m_iNumberOfThreads = h_ThreadCapacity;
    };

    void Submit(CTask _iworkItem) // Enqueue
    {
        {
            unique_lock<mutex> lock{ m_WorkQueue->m_WorkQMutex } ;
            m_WorkQueue->push(_iworkItem);
        }

        m_WorkQueue->m_WorkQCondition.notify_one();

    };

    
    void Start()
    {
        for (int i = 0u; i < m_iNumberOfThreads; i  )
        {
            m_workerThreads.emplace_back([=] {
                while (true)
                {
                    CTask task;

                    { 
                        unique_lock<mutex> lock{ m_WorkQueue->m_WorkQMutex };

                        m_WorkQueue->m_WorkQCondition.wait(lock, [=] {return m_Stopping || !m_WorkQueue->empty(); });

                        if (m_Stopping amp;amp; m_WorkQueue->empty())
                            break;

                        //if not stopping...
                        task = move(m_WorkQueue->workQ.front()); 
                        m_WorkQueue->workQ.pop(); 
                    } 

                    task.update(); // execute task
                }
            });
        }
    };

    void Stop() 
    {
        {
            lock_guard<mutex> lock{ m_WorkQueue->m_WorkQMutex };
            m_Stopping = true;
        }
        
        m_WorkQueue->m_WorkQCondition.notify_all();
        // join threads
        for (autoamp; threads : m_workerThreads)
        {
            threads.join();
        }
    };

    static Threadpoolamp; getInstance()
    {
        static Threadpool s_Instance;
        return s_Instance;
    }


private:
    //Constructor in private to create singleton
    Threadpool() {
        Initialize();
    }

    Threadpool(Threadpool constamp;) = delete; // singleton non-copyable 
    void operator = (Threadpool constamp;) = delete; // singleton un-assignable
    
protected:
    static Threadpool* s_pThreadPool; // this is for singleton, given by uni, though not gonna use it.

private:

    WorkQueue<CTask>* m_WorkQueue;

    vector<thread> m_workerThreads;

    unsigned int m_iNumberOfThreads;

    volatile bool isRunning;
    bool m_Stopping = false;

};

 

Рабочая очередь.h

 
#pragma once

#include <thread>
#include <mutex>
#include <queue>
#include <utility>
#include <vector>
#include "CTask.h"

using std::queue;
using std::lock_guard;
using std::unique_lock;
using std::mutex;
using std::condition_variable;
using std::move;

template<typename T>
class WorkQueue
{
public:
    WorkQueue() {}
    //Insert an item at the back of the queue and signal any thread that might be waiting
    //for the queue to be populated
    void push(const Tamp; item)
    {
        lock_guard<mutex> _lock(m_WorkQMutex);
        workQ.push(move(item));//copies over all the data correctly
        m_WorkQCondition.notify_one();
    }

    //Attempt to get a workitem from the queue
    //If the queue is empty just return false;
    bool nonblocking_pop(Tamp; _workItem) // use first.. ?
    {
        lock_guard<mutex> _lock(m_WorkQMutex);
        //If the queue is empty return false
        if (workQ.empty())
        {
            return false;
        }
        _workItem = move(workQ.front());
        workQ.pop();
        return true;
    }

    //Attempt to get a workitem from the queue
    //If the Q is empty just return false;
    void blocking_pop(Tamp; _workItem) // IF task still needs to be completed
    {
        unique_lock<mutex> _lock(m_WorkQMutex);
        //If the queue is empty block the thread from running until a work item
        //becomes available
        m_WorkQCondition.wait(_lock, [this] {return !workQ.empty(); });
        _workItem = move(workQ.front());
        workQ.pop();
    }

    //Checking if the queue is empty or not
    bool empty() const
    {
        lock_guard<mutex> _lock(m_WorkQMutex);
        return workQ.empty();
    }

    queue<T> workQ;
    mutable mutex m_WorkQMutex;
    condition_variable m_WorkQCondition;

private:
};

 

CTask.h

 
#pragma once
#include <iostream>
#include<Windows.h>

using std::cout;
using std::endl;

class CTask {
private:
    int sleepTimer;

public:
    CTask()//gets called at creation of an object
    {
        sleepTimer = 10;
    }; 

    ~CTask() {};

    void update()
    {
        //may be sleep
        cout << "nt...Beginning task..." << endl;
        std::this_thread::sleep_for(std::chrono::seconds(sleepTimer));
        cout << "nt   Task Complete!" << endl;
    }; 
};

 

main.cpp

 
#include <iostream>
#include <vector>
#include "WorkQueue.h"
#include "Threadpool.h"
#include "CTask.h"

using std::thread;
using std::vector;

unsigned int h_ThreadCapacity = thread::hardware_concurrency();

int main()
{
    Threadpoolamp; tPool = Threadpool::getInstance(); // create the threadpool
    CTask task; // instance of task object

    tPool.Start();
    tPool.Submit(task);

}

 

Комментарии:

1. m_WorkQueue не инициализируется.

2. Если вы используете Visual Studio, сейчас самое время научиться использовать отладчик, так как это, скорее всего, приведет вас прямо к ошибке.

3. 0x00000014 похоже, что доступ к члену с таким смещением осуществляется через нулевой указатель.

4. Всякий раз, когда вы получаете сбой, вы должны в первую очередь попытаться воспроизвести сбой. Если вы можете легко скопировать его, то вам следует использовать отладчик , чтобы поймать сбой и определить, когда и где в вашем коде это происходит.

5. В Submit at unique_lock<mutex> lock{ m_WorkQueue->m_WorkQMutex } , m_WorkQueue есть NULL . Вы отладите больше или расскажете вам об этом.