以线程安全的方式更新带增量编号的文件

时间:2013-12-24 09:18:37

标签: c# .net multithreading

我有一个我用c#编写的服务,它接受来自多个来源的请求。为每个请求分配一个ID(简单int),该ID对于每个请求都会递增。目前,递增的计数器变量是一个全局变量(很好),我按以下方式递增它(为了我的理解和测试是线程安全的):

Interlocked.Increment(ref _currentId);

有时可能会重新启动服务,在这种情况下,变量会重置为0.但重启后,我实际上需要分配的最后一个ID。

我想我应该做的是每次变量递增时将其写入文件 - 确保文件始终包含最新的id。

如何在线程安全可靠的情况下(文件始终包含最新的id)方式实现这样的事情?

此外,性能非常重要,所以我不想在IO操作上花费太多时间(我可能会将文件写入操作放在线程/任务中) - 你有什么建议?

由于

3 个答案:

答案 0 :(得分:0)

嗯,实现这一目标的最简单方法是使用lock块:

private static object syncObj = new object();
private static int counter = 0;

// Returns the incremented value
private static int Increment()
{
    lock (this.syncObj)
    {
        int currentCount = counter;
        currentCount++;

        // Write currentCount to a file here
        try
        {
            // TODO -> write operation
            // ...

            counter = currentCount;
            return currentCount;
        }
        catch (Exception)
        {
            // Do catch the exception in case the I/O operation failed
            // TODO
        }
    }
}

接下来的问题是:性能如何对您来说很重要? I / O操作(这样的文件写入)很昂贵。 lock也可能很贵。

答案 1 :(得分:0)

设置怎么样?转到项目的属性,然后选择设置选项。然后单击超链接以创建新的设置类(如果您当前没有设置)。白色Id字段的名称,设置类型和初始值。然后你可以在代码中使用它:

static readonly object Sync = new object();
public static void IncrementId()
{
    lock(Sync)
    {
        Console.WriteLine(Properties.Settings.Default.Id);
        Properties.Settings.Default.Id++;
        Properties.Settings.Default.Save();
        Console.WriteLine(Properties.Settings.Default.Id);
    }
}

答案 2 :(得分:0)

这是我能想到的最快的。

磁盘不像它看起来那么可靠。即使是正确的写也不能保持可靠。通过OS缓冲区和磁盘缓冲区传递写入可能需要很长时间。

在这段代码中,我尽可能快地写,但读取/递增速度更快。从最后一次写入到实际状态,总有可能丢失一些ID。唯一的100%解决方案是以锁定步骤进行写入和刷新,但性能明显更差。

此示例是VB-DotNet控制台应用程序。

Imports System.Threading

Module Module1

    Dim globalLong As Long = -1
    Dim globalLongLock As New Object

    Dim fileWriterThread As New Thread(AddressOf writerLoop)
    Dim fileWriterSemaphore As New SemaphoreSlim(0)


    Sub Main()
        fileWriterThread.Start()

        For i = 0 To 100
            Tasks.Parallel.For(0, 100, Sub(n) nextID())
        Next

        Console.WriteLine("done")
        Console.ReadKey()
    End Sub

    Function nextID() As Long
        '
        ' load if required
        '
        If globalLong < 0 Then         '\
            SyncLock globalLongLock    ' note the double IFs
                If globalLong < 0 Then '/

                    'TODO: load from file
                    globalLong = 1

                End If
            End SyncLock
        End If


        '
        ' increment it
        '
        Dim id = Interlocked.Increment(globalLong)


        '
        ' ask writer to store it
        '
        fileWriterSemaphore.Release()

        Return id
    End Function

    Sub writerLoop()
        Dim valueLastWritten As Long = -1

        While True
            fileWriterSemaphore.Wait()

            Dim valueToWrite = globalLong

            If valueToWrite > valueLastWritten Then

                'TODO: write 'valueToWrite' to file
                Console.WriteLine("Persisting " & valueToWrite)
                IO.File.WriteAllText("d:\n.txt", valueToWrite.ToString)

                valueLastWritten = valueToWrite
            End If
        End While
    End Sub

End Module