繁体   English   中英

多个用户在同一个文件上写入

[英]Multiple users writing at the same file

我有一个项目是一个Web API项目,我的项目由多个用户访问(我的意思是真的很多用户)。 当我的项目从前端(使用HTML 5的网页)访问,并且用户执行更新或检索数据之类的操作时,后端应用程序(Web API)将编写单个日志文件(.log文件,但内容为JSON)。 问题是,当被多个用户访问时,前端变得没有响应(总是加载)。 问题在于编写日志文件的过程(真正非常多的用户正在访问单个日志文件)。 我听说使用多线程技术可以解决问题,但我不知道哪种方法。 所以,也许任何人都可以帮助我。 这是我的代码(对不起,如果错字,我使用我的智能手机和移动版本的堆栈溢出):

public static void JsonInputLogging<T>(T m, string methodName)
{
    MemoryStream ms = new MemoryStream();
    DataContractJsonSerializer ser = new 
            DataContractJsonSerializer(typeof(T));
    ser.WriteObject(ms, m);
    string jsonString = Encoding.UTF8.GetString(ms.ToArray());
    ms.Close();
    logging("MethodName: " + methodName + Environment.NewLine + jsonString.ToString());
}


public static void logging (string message)
{
    string pathLogFile = "D:\jsoninput.log";
    FileInfo jsonInputFile = new FileInfo(pathLogFile);
    if (File.Exists(jsonInputFile.ToString()))
    {
        long fileLength = jsonInputFile.Length;
        if (fileLength > 1000000)
        {
            File.Move(pathLogFile, pathLogFile.Replace(*some new path*);
        }
    }
    File.AppendAllText(pathLogFile, *some text*);
}

你必须先了解一些内部结构。 对于每个[x]用户,ASP.Net将使用单个工作进程。 一个工作进程拥有多个线程。 如果你在云上使用多个实例,那就更糟了,因为那时你也有多个服务器实例(我认为情况并非如此)。

这里有一些问题:

  • 您有多个用户,因此有多个线程。
  • 多个线程可以相互死锁写入文件。
  • 您有多个应用程序域,因此有多个进程。
  • 多个进程可以互相锁定

打开和锁定文件

File.Open有一些用于锁定的标志。 基本上你可以按进程锁定文件,在这种情况下这是一个好主意。 使用ExistsOpen的两步法无济于事,因为在另一个工作进程之间可能会有所作为。 基本上,我们的想法是使用独占访问权限调用Open ,如果失败,请再次尝试使用其他文件名。

这基本上解决了多个进程的问题。

从多个线程写

文件访问是单线程的。 您可能希望使用单独的线程来执行文件访问,而使用多个线程来指示要写入的内容,而不是将您的内容写入文件。

如果您有比您可以处理的更多的日志请求,那么无论哪种方式,您都处于错误的区域。 在这种情况下,处理它以记录IMO的最佳方法是简单地删除数据。 换句话说,让记录器有点损失,让用户的生活更美好。 您也可以使用队列。

我通常使用ConcurrentQueue和一个单独的线程来处理所有记录的数据。

这基本上是如何做到这一点:

// Starts the worker thread that gets rid of the queue:
internal void Start()
{
    loggingWorker = new Thread(LogHandler)
    {
        Name = "Logging worker thread",
        IsBackground = true,
        Priority = ThreadPriority.BelowNormal
    };
    loggingWorker.Start();
}

我们还需要做一些事情来完成实际工作和一些共享的变量:

private Thread loggingWorker = null;
private int loggingWorkerState = 0;
private ManualResetEventSlim waiter = new ManualResetEventSlim();
private ConcurrentQueue<Tuple<LogMessageHandler, string>> queue =
    new ConcurrentQueue<Tuple<LogMessageHandler, string>>();

private void LogHandler(object o)
{
    Interlocked.Exchange(ref loggingWorkerState, 1);

    while (Interlocked.CompareExchange(ref loggingWorkerState, 1, 1) == 1)
    {
        waiter.Wait(TimeSpan.FromSeconds(10.0));
        waiter.Reset();

        Tuple<LogMessageHandler, string> item;
        while (queue.TryDequeue(out item))
        {
            writeToFile(item.Item1, item.Item2);
        }
    }
}

基本上,这段代码使您可以使用跨线程共享的队列来处理单个线程中的所有项目。 请注意, ConcurrentQueue不使用TryDequeue锁,因此客户端不会因此而感到任何痛苦。

最后需要的是将东西添加到队列中。 这很简单:

public void Add(LogMessageHandler l, string msg)
{
    if (queue.Count < MaxLogQueueSize) 
    {
        queue.Enqueue(new Tuple<LogMessageHandler, string>(l, msg));
        waiter.Set();
    }
}

此代码将从多个线程调用。 这不是100%正确,因为CountEnqueue不一定必须以一致的方式调用 - 但是对于我们的意图和目的而言,它已经足够好了。 它也不会锁定Enqueuewaiter会确保其他线程删除了这些东西。

用单例模式包装所有这些,为它添加更多逻辑,你的问题应该解决。

这可能会有问题,因为无论如何,默认情况下每个客户端请求都由新线程处理。 您需要一些在项目中已知的“根”对象(不要认为您可以在静态类中实现此目的),因此您可以在访问日志文件之前锁定它。 但请注意,它基本上会对请求进行序列化,并且可能会对性能产生非常不利的影响。

没有多线程无法解决您的问题。 多线程如何同时写入同一个文件? 你需要关心数据的一致性 ,我不认为这是实际的问题。

你搜索的是异步编程。 GUI无法响应的原因是,它等待任务完成。 如果你知道,记录器是你的瓶颈,然后使用异步你的优势。 触发日志方法并忘记结果,只需编写文件即可。

实际上我并不认为你的记录器是问题。 你确定没有其他阻止你的逻辑吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM