2017-02-23 105 views
1

Я пишу небольшую программу, которая выполняет всю операцию записи в основной программе. Данные представляют собой структуры с не постоянными размерами. Их много, и новые будут добавлены в будущем. Из-за этого я решил работать с массивами char и передать указатель на эти массивы от метода к методу.Нарушение доступа при записи массива символов в файл

70% времени, когда код работает так, как предполагалось, но я часто получаю ошибки «нарушения прав доступа» при записи массива, и иногда эта ошибка возникает из-за количества сервлетов в строке. Я просто не могу найти шаблон.

Следует отметить, что Execute работает в другом потоке.

struct BufferElement 
{ 
    char* ptrData; 
    int TotalBytes; 
    int StartPosition; 
    std::string FileName; 
}; 

class FileThread 
{ 
private: 
    std::vector<BufferElement> Queue; 
    bool BoolThread; 
    std::ofstream writestream; 

//This Method calls the "WriteInFile" Method for the first element in the 
//Queue and erases it from the vector 
void Execute(void) 
{ 
    while(BoolThread) 
    { 
    if(Queue.size() > 0) 
    { 
     if(WriteInFile()) 
     { 
     delete[] Queue.at(0).ptrData; 
     Queue.erase(Queue.begin()); 
     } 
    } 
    } 
} 

//This Method writes the first Element of the Queue in the file 
bool WriteInFile(void) 
{ 
    if(Queue.at(0).ptrData == NULL) 
    { 
    return true; 
    } 

    writestream.open(Queue.at(0).FileName.c_str(), std::ios::in | 
         std::ios::out | std::ios::binary); 

    if(!writestream.is_open()) 
    { 
    writestream.close(); 
    writestream.clear(); 
    return false; 
    } 

    writestream.seekp(Queue.at(0).StartPosition); 
    writestream.write(Queue.at(0).ptrData, Queue.at(0).TotalBytes); 

    writestream.close(); 
    writestream.clear(); 

return true; 
} 

public: 
void EndThread(void) 
{ 
    BoolThread = false; 
    for(int i = 0; i < Queue.size(); i++) 
    { 
    delete[] Queue.at(i).ptrData; 
    } 
} 

template< typename T > 
void WriteOrder(std::string _FileName, T _Data, int _StartPosition) 
{ 
    BufferElement Temp_BufferElement; 
    Temp_BufferElement.TotalBytes = sizeof(_Data); 
    Temp_BufferElement.StartPosition = _StartPosition; 
    Temp_BufferElement.FileName = _FileName; 

    Temp_BufferElement.ptrData = new char[ Temp_BufferElement.TotalBytes ]; 
    memcpy(Temp_BufferElement.DataPtr, _Data, Temp_BufferElement.TotalBytes); 

    Queue.push_back(Temp_BufferElement); 
} 
}; 

int main(void) 
{ 
    std::string Path = "..\\Data\\Test.dat"; 
    FileThread Writer; 

    for(int i = 0; i < 1000; i++) 
    { 
    char array[] = {'H','e','l','l','o',' ','W','o','r','l','d','!','\0'}; 
    Writer.WriteOrder(Path, array, i * sizeof(array); 
    } 

    system("pause"); 
    Writer.EndThread(); 
    return 0; 
} 

Я был бы рад, если бы кто-нибудь мог взглянуть на код. Может быть, я просто что-то пропустил. Я использую Borland Turbo C++ Builder, а Thread - это объект из класса vcl.

+1

Я предполагаю, что 'EndThread' вызывается в другом потоке, чем' Execute'? И я вообще не вижу синхронизации. – knivil

+0

Да. Его вызвали из основного потока. Необходима ли синхронизация? Только метод Execute имеет доступ к файлу – Schmelix

+0

Почему бы не предоставить полный код создания и завершения потоков? – user

ответ

2

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

Я также не вижу, где BoolThread инициализирован или изменен. Таким образом, либо этот пример неполный, либо он может вести себя странно.

+0

Добавлены некоторые блокирующие блоки для вектора, и теперь он работает даже на 100-й итерации. Спасибо – Schmelix

+0

Вы также должны проверить проблему 'sizeof (Data)', так как это приведет к тому, что другие побочные эффекты не будут сразу заметны. – Devolus

0
void WriteOrder(std::string _FileName, T _Data, int _StartPosition) 
{ 
    BufferElement Temp_BufferElement; 
    Temp_BufferElement.TotalBytes = sizeof(_Data); 
    Temp_BufferElement.StartPosition = _StartPosition; 
    Temp_BufferElement.FileName = _FileName; 

    Temp_BufferElement.ptrData = new char[ Temp_BufferElement.TotalBytes ]; 
    memcpy(Temp_BufferElement.DataPtr, _Data, Temp_BufferElement.TotalBytes); 

    Queue.push_back(Temp_BufferElement); 
} 

При вызове такой:

Writer.WriteOrder(Path, array, i * sizeof(array)); 

Temp_BufferElement.TotalBytes проведет 4 (или 8) (SizeOf (_data)), казалось бы, это не то, что вы хотели. Эта линия:

Temp_BufferElement.ptrData = new char[ Temp_BufferElement.TotalBytes ]; 
     memcpy(Temp_BufferElement.DataPtr, _Data, Temp_BufferElement.TotalBytes); 

будет выделять на кучу и копировать 4 байта (в зависимости от размера указателя, 32/64 бит программы)

Этот код:

Queue.push_back(Temp_BufferElement); 

До этого кода является выполняется, если Queue.size() == Queue.capacity(), вектор перераспределяет себя, вызывая проблему синхронизации с потоком, выполняющим функцию «Выполнять».