0

我有一个应用程序,我需要在其中创建具有唯一序列号的文件作为文件名的一部分。我的第一个想法是使用(因为这个应用程序没有任何其他数据存储)一个包含数字的文本文件,我会增加这个数字,这样我的应用程序总是会创建一个具有唯一 ID 的文件。然后我想,也许在有多个用户同时提交给这个应用程序的时候,一个进程可能在它被前一个进程写入之前正在读取 txt 文件。所以我正在寻找一种在同一个“进程”中读取和写入文件的方法(使用 try catch 这样我就可以知道它何时被另一个进程使用,然后等待并尝试从它读取几次) ' 没有在两者之间解锁文件。如果我上面所说的听起来是一个糟糕的选择,你能给我一个替代方案吗?然后,您将如何跟踪像我的案例这样的应用程序的唯一标识号?

谢谢。

4

3 回答 3

1

如果它是单个应用程序,那么您可以将当前号码存储在您的应用程序设置中。在启动时加载该号码。然后对于每个请求,您可以安全地增加它并使用结果。程序关闭时保存序号。例如:

private int _fileNumber;

// at application startup
_fileNumber = LoadFileNumberFromSettings();

// to increment
public int GetNextFile()
{
    return Interlocked.Increment(ref _fileNumber);
}

// at application shutdown
SaveFileNumberToSettings(_fileNumber);

或者,您可能希望确保在文件编号增加时保存文件编号。如果是这样,请更改您的GetNextFile方法:

private readonly object _fileLock = new object();
public int GetNextFile()
{
    lock (_fileLock)
    {
        int result = ++_fileNumber;
        SaveFileNumbertoSettings(_fileNumber);
        return result;
    }
}

另请注意,为此使用注册表而不是文件可能是合理的。

于 2013-10-15T19:15:16.597 回答
0

我更喜欢具有全局互斥锁的复杂且通用的解决方案。它使用一个名称以“Global\”为前缀的互斥锁,这使得它在系统范围内,即一个互斥锁实例在所有进程之间共享。如果您的程序在友好的环境中运行,或者您可以指定限制为您可以信任的用户帐户的严格权限,那么它运行良好。

请记住,此解决方案不是事务性的,并且不受线程中止/进程终止的保护。

非事务性意味着如果您的进程/线程在存储文件修改过程中被捕获并被终止/中止,那么存储文件将处于未知状态。例如,它可以留空。您可以通过首先写入新值、保存文件然后删除之前的值来保护自己免受数据丢失(上次使用的索引丢失)。读取过程应该期望一个具有多个数字的文件,并且应该取最大的。

不受线程中止保护意味着如果获得互斥锁的线程意外中止和/或您没有适当的异常处理,则互斥锁可能会在创建该线程的进程的生命周期内保持锁定状态。为了使解决方案受到中止保护,您必须在获取锁时实现超时,即替换永远等待的以下行

blnResult = iLock.Mutex.WaitOne();

有超时的东西。

总结一下,我想说的是,如果您正在寻找一个真正强大的解决方案,您将开始使用某种事务数据库或自己编写一种这样的数据库:)

这是没有超时处理的工作代码(我的解决方案中不需要它)。一开始就足够强大。

using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;

namespace ConsoleApplication31
{
    class Program
    {
        //You only need one instance of that Mutex for each application domain (commonly each process).
        private static SMutex mclsIOLock;

        static void Main(string[] args)
        {
            //Initialize the mutex. Here you need to know the path to the file you use to store application data.
            string strEnumStorageFilePath = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                "MyAppEnumStorage.txt");
            mclsIOLock = IOMutexGet(strEnumStorageFilePath);
        }

        //Template for the main processing routine.
        public static void RequestProcess()
        {
            //This flag is used to protect against unwanted lock releases in case of recursive routines.
            bool blnLockIsSet = false;

            try
            {
                //Obtain the lock.
                blnLockIsSet = IOLockSet(mclsIOLock);

                //Read file data, update file data. Do not put much of long-running code here.
                //Other processes may be waiting for the lock release.
            }
            finally
            {
                //Release the lock if it was obtained in this particular call stack frame.
                IOLockRelease(mclsIOLock, blnLockIsSet);
            }

            //Put your long-running code here.
        }

        private static SMutex IOMutexGet(string iMutexNameBase)
        {
            SMutex clsResult = null;
            clsResult = new SMutex();

            string strSystemObjectName = @"Global\" + iMutexNameBase.Replace('\\', '_');

            //Give permissions to all authenticated users.
            SecurityIdentifier clsAuthenticatedUsers = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
            MutexSecurity clsMutexSecurity = new MutexSecurity();
            MutexAccessRule clsMutexAccessRule = new MutexAccessRule(
                clsAuthenticatedUsers,
                MutexRights.FullControl,
                AccessControlType.Allow);
            clsMutexSecurity.AddAccessRule(clsMutexAccessRule);

            //Create the mutex or open an existing one.
            bool blnCreatedNew;
            clsResult.Mutex = new Mutex(
                false,
                strSystemObjectName,
                out blnCreatedNew,
                clsMutexSecurity);
            clsResult.IsMutexHeldByCurrentAppDomain = false;

            return clsResult;
        }

        //Release IO lock.
        private static void IOLockRelease(
            SMutex iLock,
            bool? iLockIsSetInCurrentStackFrame = null)
        {
            if (iLock != null)
            {
                lock (iLock)
                {
                    if (iLock.IsMutexHeldByCurrentAppDomain &&
                        (!iLockIsSetInCurrentStackFrame.HasValue ||
                        iLockIsSetInCurrentStackFrame.Value))
                    {
                        iLock.MutexOwnerThread = null;
                        iLock.IsMutexHeldByCurrentAppDomain = false;
                        iLock.Mutex.ReleaseMutex();
                    }
                }
            }
        }

        //Set the IO lock.
        private static bool IOLockSet(SMutex iLock)
        {
            bool blnResult = false;

            try
            {
                if (iLock != null)
                {
                    if (iLock.MutexOwnerThread != Thread.CurrentThread)
                    {
                        blnResult = iLock.Mutex.WaitOne();
                        iLock.IsMutexHeldByCurrentAppDomain = blnResult;

                        if (blnResult)
                        {
                            iLock.MutexOwnerThread = Thread.CurrentThread;
                        }
                        else
                        {
                            throw new ApplicationException("Failed to obtain the IO lock.");
                        }
                    }
                }
            }
            catch (AbandonedMutexException iMutexAbandonedException)
            {
                blnResult = true;
                iLock.IsMutexHeldByCurrentAppDomain = true;
                iLock.MutexOwnerThread = Thread.CurrentThread;
            }

            return blnResult;
        }
    }

    internal class SMutex
    {
        public Mutex Mutex;
        public bool IsMutexHeldByCurrentAppDomain;
        public Thread MutexOwnerThread;
    }
}
于 2013-10-15T19:14:06.127 回答
0

Edit: As Alireza pointed in the comments, it is not a valid way to lock between multiple applications.

You can always lock the access to the file (so you won't need to rely on exceptions). e.g:

// Create a lock in your class
private static object LockObject = new object();

// and then lock on this object when you access the file like this:
lock(LockObject)
{
    ... access to the file
}

Edit2: It seems that you can use Mutex to perform inter-application signalling.

private static System.Threading.Mutex m = new System.Threading.Mutex(false, "LockMutex");

void AccessMethod()
{
    try
    {
        m.WaitOne();
        // Access the file

    }
    finally
    {
        m.ReleaseMutex();
    }
}

But it's not the best pattern to generate unique ids. Maybe a sequence in a database would be better ? If you don't have a database, you can use Guids or a local database (even Access would be better I think)

于 2013-10-15T18:58:36.043 回答