неважно. Я не заметил, что его синглтон является частным и используется внутри методов write и writeline.

я есть многопоточное приложение для сканирования портов, написанное на C #, и я хочу напечатать некоторые вещи как в консоль, так и в файл журнала при запуске приложения. По этой причине у меня есть следующий вспомогательный класс, который прекрасно работает при записи как в файл журнала, так и в консоль.

public class Output
{
    private const string LOG_DIRECTORY = "Logs";
    private readonly string LogDirPath = Path.Combine(Directory.GetCurrentDirectory(), LOG_DIRECTORY);

    private static Output _outputSingleton;
    private static Output OutputSingleton {
        get {
            if (_outputSingleton == null)
            {
                _outputSingleton = new Output();
            }
            return _outputSingleton;
        }
    }

    public StreamWriter SW { get; set; }

    public Output()
    {
        EnsureLogDirectoryExists();
        InstantiateStreamWriter();
    }

    ~Output()
    {
        if (SW != null)
        {
            try
            {
                SW.Dispose();
            }
            catch (ObjectDisposedException) { } // object already disposed - ignore exception
        }
    }

    public static void WriteLine(string str)
    {
        Console.WriteLine(str);
        OutputSingleton.SW.WriteLine(str);
    }

    public static void Write(string str)
    {
        Console.Write(str);
        OutputSingleton.SW.Write(str);
    }

    private void InstantiateStreamWriter()
    {
        long ticks = DateTime.Now.Ticks;
        string logFilename = "scan_" + ticks.ToString() + ".txt";
        string filePath = Path.Combine(LogDirPath, logFilename);
        try
        {
            SW = new StreamWriter(filePath);
            SW.AutoFlush = true;
        }
        catch (UnauthorizedAccessException ex)
        {
            throw new ApplicationException(string.Format("Access denied. Could not instantiate StreamWriter using path: {0}.", filePath), ex);
        }
    }

    private void EnsureLogDirectoryExists()
    {
        if (!Directory.Exists(LogDirPath))
        {
            try
            {
                Directory.CreateDirectory(LogDirPath);
            }
            catch (UnauthorizedAccessException ex)
            {
                throw new ApplicationException(string.Format("Access denied. Could not create log directory using path: {0}.", LogDirPath), ex);
            }
        }
    }
}

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

 Magnetron27 дек. 2017 г., 18:57
Используйте постоянное имя файла вместо того, чтобы создавать новое при каждом доступе к функции. Затем используйтеlock написать ему. Или создатьConcurrentBag<string>, добавьте журналы и в конце запишите его в файл.
 tinker27 дек. 2017 г., 18:59
@Magnetron Моя задача - написать их во время работы программы, я писал ранее в конце, но мне было предложено изменить ее. Кроме того, имя файла должно в основном иметь имя, которое включает DateTime в тиках при создании файла.
 Magnetron27 дек. 2017 г., 19:03
Затем, когда программа запустится, создайте имя файла, которое будет использоваться для всех потоков. Когда вы собираетесь писать, используйтеlock чтобы гарантировать, что только 1 поток имеет доступ к нему.

Ответы на вопрос(1)

Решение Вопроса

Использование одного файла иlock Стратегия должна делать работу:

private Object m_Lock = new Object();

public static void WriteLine(string str)
{
    lock (m_Lock)
    {
        Console.WriteLine(str);
        OutputSingleton.SW.WriteLine(str);
    }
}

public static void Write(string str)
{
    lock (m_Lock)
    {
        Console.Write(str);
        OutputSingleton.SW.Write(str);
    }
}

private void InstantiateStreamWriter()
{
    string logFilename = "Log.txt";
    string filePath = Path.Combine(LogDirPath, logFilename);

    try
    {
        SW = new StreamWriter(filePath);
        SW.AutoFlush = true;
    }
    catch (UnauthorizedAccessException ex)
    {
        throw new ApplicationException(string.Format("Access denied. Could not instantiate StreamWriter using path: {0}.", filePath), ex);
    }
}

Проблема здесь идет с общей блокировкой. Если вы используете одну и ту же блокировку для нескольких методов, то блокировка одного метода блокирует и другие методы (в вашем случаеWrite а такжеWriteLine). Это выглядит вполне нормально для меня, поскольку они строго связаны между собой ... но это может создать узкое место, если методы вызываются очень часто. С другой стороны, отдельные блокировки заставляют методы работать независимо, и это еще хуже.

Попробуйте объединить два метода в один следующим образом, чтобы вам не приходилось обрабатывать блокировки для отдельных методов:

public static void WriteLine(String str, Boolean line = true)
{
    lock (m_Lock)
    {
        if (line)
        {
            Console.WriteLine(str);
            OutputSingleton.SW.WriteLine(str);
        }
        else
        {
            Console.Write(str);
            OutputSingleton.SW.Write(str);
        }
    }
}
 Tommaso Belluzzo27 дек. 2017 г., 19:17
Извините, неправильно набрал название объекта, я исправлю это сейчас!
 Tommaso Belluzzo27 дек. 2017 г., 19:24
Ах, хорошее место ... Я не думаю, что это может вызвать проблемы, пока методы заблокированы. На мой взгляд, здесь лучше использовать не синглтонный, полностью статический паттерн.
 ashbygeek27 дек. 2017 г., 19:22
Разве блокировка также не должна использоваться в OutputSingleton Getter? Похоже, его два потока вызвали возврат двух разных объектов Output.
 ashbygeek27 дек. 2017 г., 19:28
неважно. Я не заметил, что его синглтон является частным и используется внутри методов write и writeline.

Ваш ответ на вопрос