Совместное использование сетевых файлов для записи и чтения - PullRequest
0 голосов
/ 05 сентября 2018

У меня клиент-серверное приложение, разработанное на C # .NET 3.5. Это приложение использует 3 программы для выполнения необходимой работы. Процесс A (сервер) и процесс B (удаленный запуск) разработаны в .NET, а процесс C - это стороннее консольное приложение, язык разработки которого неизвестен. Используя административные учетные данные удаленного компьютера, процесс A копирует на него процесс B и планирует процесс B как задачу на удаленном компьютере. После этого процесс B запускается планировщиком задач и создает файл log.txt для регистрации сообщений. Затем Процесс B запускает Процесс C, используя семантику Process.Start(), и перенаправляет его standard output and error для записи в файл log.txt. Процесс A использует семантику Process.GetProcesses(remotecomputername), чтобы отслеживать, работает ли еще процесс C на удаленном компьютере. Процесс A также читает файл log.txt, используя сетевой ресурс, читаемый как \\RemoteComputerName\C$\RemoteDir\log.txt, и отображает сообщения в своем окне.

Моя проблема в том, что все выходные данные и сообщения об ошибках не регистрируются log.txt. И Процесс A не может правильно читать из log.txt. Если выходные данные / ошибки регистрируются с помощью DebugView, они регистрируются правильно. Является ли это проблемой синхронизации / прав доступа? Как от этого избавиться? Любые указатели / подсказки будут действительно ценными. Невозможно предоставить полный код из-за ограничений.

Пример кода приведен ниже

Процесс A

//Below method is called every 2 seconds from Server to read new messages.
//path passed as `\\RemoteComputerName\C$\RemoteDir\log.txt`
private void ReadRemoteLog(string path)
{
    try
    {
        string[] lines = File.ReadAllLines(path);
        while (RemoteLogPosition < lines.LongLength)
        {
            string msg = lines[RemoteLogPosition].Trim();
            if (!string.IsNullOrEmpty(msg))
            {
                Trace.WriteLine("# " +msg); //Writing on DebugView
                OnProgressChanged(msg);
            }
            RemoteLogPosition++; //This is global variable to keep track of last read position.
        }      
    }   
}

Код процесса B для запуска процесса C

ProcessStartInfo ps = new ProcessStartInfo();
ps.UseShellExecute = false;
ps.FileName = <Path to process C>;
ps.Arguments = <Commandline args to Process C>;
ps.WorkingDirectory = @"C:\RemoteDir";
ps.RedirectStandardError = true;
ps.RedirectStandardOutput = true;

Process p = new Process();
p.StartInfo = ps;

p.OutputDataReceived += (s, e) => { WriteLog(e.Data.Trim());};
p.ErrorDataReceived += (s, e) => { WriteLog(e.Data.Trim()); };
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
WriteLog("Process Started - "+ps.FileName + ps.Arguments);
p.WaitForExit();

Метод WriteLog процесса B -

private void WriteLog(string message)
{
    using (FileStream fs = new FileStream(@"C:\\RemoteDir\log.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Inheritable))
    using(StreamWriter sw = new StreamWriter(fs))
    {
        sw.WriteLine("#" + message);
    }
}

Ответы [ 3 ]

0 голосов
/ 14 сентября 2018

Краткий ответ: File.ReadAllLines не позволит другому процессу записать в файл. Связанный вопрос SO, который имеет некоторые обходные пути: File.ReadLines без блокировки?

Тот факт, что ReadAllLines открывает файл с FileShare.Read, можно найти, посмотрев исходный код .Net: https://referencesource.microsoft.com/#mscorlib/system/io/file.cs,5150c565446cd5fd

Отслеживая вызовы, он в конечном итоге достигнет:

Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);

FileShare.Read говорит

Позволяет открывать файл для последующего чтения. Если этот флаг не указан, любой запрос на открытие файла для чтения (этим процессом или другим процессом) не будет выполнен, пока файл не будет закрыт.

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

По сути, вам нужно открыть файл с помощью FileShare.ReadWrite (в вашем процессе A), чтобы позволить другому процессу (вашему процессу C) продолжить запись этого файла.

0 голосов
/ 15 сентября 2018

Вы можете попробовать использовать объект в качестве блокировки при записи в файл:

private static object fileLock = new Object();

Теперь в вашем WriteLog методе: вы можете использовать lock(fileLock) {}, используя, чтобы избавиться от потока.

Также вы можете взглянуть на EventWaitHandle . Это позволяет создать экземпляр дескриптора ожидания для каждого процесса.

var wait = new EventWaitHandle(true, EventResetMode.AutoReset, "Common_to_all_process");

Затем, после завершения первого процесса, он может передать его следующему процессу, сообщив:

wait.WaitOne();
/* write to the file*/
wait.Set();

Можно использовать GUID для «Common_to_all_process».

0 голосов
/ 13 сентября 2018

Помните, что файл получает эксклюзивную блокировку при записи. Так что, если два потока пишут одновременно, один из них попадет в исключение. Чтение не проблема. Простое решение - создать несколько файлов журнала или использовать базу данных. Другой способ - продолжать накапливать сообщения и писать один раз в час или когда их количество достигает 100. Составить список строк

List<string> messagelst= new List<string>();
private void WriteLog(string message)
{


        messagelst.Add("New York");
        messagelst.Add("Mumbai");
        messagelst.Add("Berlin");
        messagelst.Add("Istanbul");
if(messagelst.length==100){
string message= string.Join(",", messagelst.ToArray());

    using (FileStream fs = new FileStream(@"C:\\RemoteDir\log.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Inheritable))
    using(StreamWriter sw = new StreamWriter(fs))
    {
        sw.WriteLine("#" + message);
    }

messagelst= new List<string>();

}
}

Другой способ - написать асинхронно. Вы можете потерять последовательность.

static Task ProcessWrite()
{
    string filePath = @"c:\temp2\temp2.txt";
    string text = "Hello World\r\n";

    return WriteTextAsync(filePath, text);
}

static async Task WriteTextAsync(string filePath, string text)
{
    byte[] encodedText = Encoding.Unicode.GetBytes(text);

    using (FileStream sourceStream = new FileStream(filePath,
        FileMode.Append, FileAccess.Write, FileShare.None,
        bufferSize: 4096, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...