Можно ли использовать данный класс в многопоточной среде безопасно?

Как создать в безопасном многопоточном окружении класс для вывода информации о работе программы в консоль и в файл? Какие методы можно использовать для обеспечения безопасности работы класса в многопоточной среде? Желаемо сохранить цельные сообщения при выводе информации.
  • 28 июня 2024 г. 6:32
Ответы на вопрос 2
Этот класс может быть использован в многопоточной среде безопасно, если обеспечить синхронизацию доступа к его методам. 

Для создания класса, который может выводить информацию как в консоль, так и в файл в безопасном многопоточном окружении, можно использовать концепцию мьютексов или блокировок. 

Вот пример простого класса, который выполняет такую задачу:
```java
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Logger {
    private PrintWriter consoleWriter;
    private PrintWriter fileWriter;
    private Lock lock = new ReentrantLock();

    public Logger(String filename) {
        try {
            consoleWriter = new PrintWriter(System.out, true);
            fileWriter = new PrintWriter(new FileWriter(filename), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void log(String message) {
        lock.lock();
        try {
            consoleWriter.println(message);
            fileWriter.println(message);
        } finally {
            lock.unlock();
        }
    }

    public void close() {
        consoleWriter.close();
        fileWriter.close();
    }
}
```

В данном примере используется мьютекс `ReentrantLock`, чтобы обеспечить синхронизированный доступ к методу `log()`, который выводит информацию как в консоль, так и в файл. Мьютекс блокирует доступ к методу другим потокам, пока один поток не завершит свою операцию.

Таким образом, использование блокировок или мьютексов позволяет обеспечить безопасность работы класса в многопоточной среде и сохранить цельные сообщения при выводе информации.
Не безопасен. 
Вы защитили мьютексом только запись в очередь (push), но не чтение из нее (pop). Так что очередь сломается рано или поздно.
Вообще тут лучше бы подошла какая-то специализированная структура со встроенной поддержкой многопоточности, а не стандартные контейнеры.
Можно использовать кольцевой буфер или очередь майкла-скотта. В booste, на сколько помню, есть и то и другое.
Но для начала сгодится и такой вариант.

Кроме того для вывода на экран в Линукс можно учитывать тот факт, что ОС обеспечивает атомарную запись в консоль для буфера менее PIPE_BUF байт. https://linux.die.net/man/7/pipe
Думаю в винде то же есть похожая гарантия, но это не точно.

Кроме того, операция << для логгирования не очень подходит, т.к. вынуждает использовать конструкции типа:
oss << "Error: " << strerror(err) << "(" << err << ")";

для вывода 1 строки. А это уже не одна запись в очередь, а несколько. Так что защита мьютексом тут не будет гарантией вывода целостной строки.
Для логгера больше подойдет вариант типа std::printf, когда в один вызов передается вся информация для генерации целой строки.
Похожие вопросы