我有一个应用程序,我需要创建具有唯一序列号的文件作为文件名的一部分。我的第一个想法是使用(因为这个应用程序没有任何其他数据存储)一个包含数字的文本文件,我会增加这个数字,所以我的应用程序将始终创建一个具有唯一ID的文件。 然后我想,也许在有多个用户同时提交到这个应用程序的时候,一个进程可能正在读取前一个进程写入之前的txt文件。那么我正在寻找一种方法来读取和写入一个文件(使用try catch,然后我可以知道它何时被另一个进程使用,然后等待并尝试从其他几次读取)在同一个进程中'没有在两者之间解锁文件。 如果我上面说的话听起来不是一个糟糕的选择,你可以给我一个替代方案吗?那么你如何跟踪像我这样的应用程序的唯一标识号?
感谢。
答案 0 :(得分: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;
}
}
另请注意,使用注册表可能是合理的,而不是文件。
答案 1 :(得分:0)
编辑:正如Alireza在评论中指出的那样,它不是锁定多个应用程序的有效方法。
您始终可以lock
访问该文件(因此您无需依赖异常)。
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:您似乎可以使用Mutex
来执行应用程序间信令。
private static System.Threading.Mutex m = new System.Threading.Mutex(false, "LockMutex");
void AccessMethod()
{
try
{
m.WaitOne();
// Access the file
}
finally
{
m.ReleaseMutex();
}
}
但它不是生成唯一ID的最佳模式。也许数据库中的序列会更好?如果您没有数据库,可以使用Guid
或本地数据库(我认为甚至可以访问Access)
答案 2 :(得分: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;
}
}