线程的 C# 锁定文件 XML
C# lock file XML for threads
我对锁定声明有疑问
我看到的所有例子,锁定语句以相同的形式用于全局变量,但在我的情况下没有发生,那么我不知道如何避免错误"file is used for another..."
我的情况。
用户转到 form1,并在 form1 中向 XML 添加行。而在第二个平面中,线程获取 XML 并将信息发送到服务器并更新此 XML 中的状态对象。
然后当用户和线程同时写入时(一个由用户写入,另一个由第二个平面写入,xml 保存抛出错误)
我正在这样做
form1
{
var A = takeAllABCObjects();
var A = A + new objects();
saveABCObjects(A);
}
Thread in second plane in all time life of app
{
var B = takeAllABCObjects();
B = UpdateObjectsB();
saveABCObjects(B);
}
Class saveABCObjects(list<objects> ABCObjects)
{
lock (ABCObjects)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<objects>));
TextWriter textWriter = new StreamWriter("ABC.xml");
serializer.Serialize(textWriter, ABCObjects);
textWriter.Close();
}
}
那么我的问题是...
lock语句使用错误?
我应该如何使用它?
Tnx,对不起我的工程师
如果您使用写权限和 "read" 共享权限打开文件,您应该没问题
所以 processA 打开写入
using(FileStream fs = File.Open("file.ext",FileMode.Open,FileAccess.Write,FileShare.Read))
进程 B 打开以供读取
using(FileStream fs = File.Open("file.ext",FileMode.Open,FileAccess.Read,FileShare.ReadWrite))
添加了一些代码,但它应该允许一个进程在另一个进程读取的同时写入
至于reader没过完就是另外一回事了
我会使用 wrapper
class 访问您的 XML
因为你的两个线程不知道彼此何时访问文件,让 class 自己做。
我在想这样的事情:
public class SynchronizedXMLAccess : IDisposable
{
private static readonly Mutex _access = new Mutex();
public FileStream Fs { get; private set; }
public SynchronizedXMLAccess(String path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.ReadWrite, FileShare sharing = FileShare.None)
{
_access.WaitOne();
Fs = File.Open(path, mode, access, sharing);
}
#region Implementation of IDisposable
public void Dispose()
{
Fs.Close();
_access.ReleaseMutex();
}
#endregion
}
}
您需要实例化 class 才能访问文件,但这将确保它只在线程之间打开一次。由于它实现了 IDisposable
,您将能够使用 using
语句
实例化它
这是最基本的形式,所以如果你使用它,你将无法访问 2 个不同的文件,只能跨线程打开一个文件
编辑:
用法示例:
Class saveABCObjects(list<objects> ABCObjects)
{
using(var sync = new SynchronizedXMLAccess(...))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<objects>));
TextWriter textWriter = new StreamWriter(sync.Fs);
serializer.Serialize(textWriter, ABCObjects);
textWriter.Close();
}
}
基本问题是您的线程和窗体没有使用同一个锁对象。我不知道你是如何创建线程的,但我已经编写了一个带有 2 个任务的小控制台应用程序,这些任务将锁定并交替写入和读取文件。您必须将相同的锁定对象传递给每个任务才能使其工作。
static void Main(string[] args)
{
var lockObject = new object();
var fileName = @"C:\Users\kevin\Documents\test.txt";
Action<object> action1 = (o) =>
{
var i = 0;
while (i++ < 1000)
{
// do stuff that doesn't require the lock
lock (o)
{
Console.WriteLine("In Thread1");
// do stuff that does require the lock
var text = File.ReadAllText(fileName);
Console.WriteLine(text);
File.WriteAllText(fileName, "\tThread1");
}
}
};
// Pass in our shared lock object
Task task1 = new Task(action1, lockObject);
task1.Start();
Action<object> action2 = (o) =>
{
var i = 0;
while (i++ < 1000)
{
// do stuff that doesn't require the lock
lock (o)
{
// do stuff that does require the lock
Console.WriteLine("In Thread2");
var text = File.ReadAllText(fileName);
Console.WriteLine(text);
File.WriteAllText(fileName, "\tThread2");
}
}
};
// Pass in our shared lock object
Task task2 = new Task(action2, lockObject);
task2.Start();
// sleep main thread and let the 2 threads do their stuff
Thread.Sleep(5000);
Console.ReadLine();
}
我对锁定声明有疑问
我看到的所有例子,锁定语句以相同的形式用于全局变量,但在我的情况下没有发生,那么我不知道如何避免错误"file is used for another..."
我的情况。 用户转到 form1,并在 form1 中向 XML 添加行。而在第二个平面中,线程获取 XML 并将信息发送到服务器并更新此 XML 中的状态对象。 然后当用户和线程同时写入时(一个由用户写入,另一个由第二个平面写入,xml 保存抛出错误) 我正在这样做
form1
{
var A = takeAllABCObjects();
var A = A + new objects();
saveABCObjects(A);
}
Thread in second plane in all time life of app
{
var B = takeAllABCObjects();
B = UpdateObjectsB();
saveABCObjects(B);
}
Class saveABCObjects(list<objects> ABCObjects)
{
lock (ABCObjects)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<objects>));
TextWriter textWriter = new StreamWriter("ABC.xml");
serializer.Serialize(textWriter, ABCObjects);
textWriter.Close();
}
}
那么我的问题是... lock语句使用错误? 我应该如何使用它?
Tnx,对不起我的工程师
如果您使用写权限和 "read" 共享权限打开文件,您应该没问题
所以 processA 打开写入
using(FileStream fs = File.Open("file.ext",FileMode.Open,FileAccess.Write,FileShare.Read))
进程 B 打开以供读取
using(FileStream fs = File.Open("file.ext",FileMode.Open,FileAccess.Read,FileShare.ReadWrite))
添加了一些代码,但它应该允许一个进程在另一个进程读取的同时写入
至于reader没过完就是另外一回事了
我会使用 wrapper
class 访问您的 XML
因为你的两个线程不知道彼此何时访问文件,让 class 自己做。
我在想这样的事情:
public class SynchronizedXMLAccess : IDisposable
{
private static readonly Mutex _access = new Mutex();
public FileStream Fs { get; private set; }
public SynchronizedXMLAccess(String path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.ReadWrite, FileShare sharing = FileShare.None)
{
_access.WaitOne();
Fs = File.Open(path, mode, access, sharing);
}
#region Implementation of IDisposable
public void Dispose()
{
Fs.Close();
_access.ReleaseMutex();
}
#endregion
}
}
您需要实例化 class 才能访问文件,但这将确保它只在线程之间打开一次。由于它实现了 IDisposable
,您将能够使用 using
语句
这是最基本的形式,所以如果你使用它,你将无法访问 2 个不同的文件,只能跨线程打开一个文件
编辑:
用法示例:
Class saveABCObjects(list<objects> ABCObjects)
{
using(var sync = new SynchronizedXMLAccess(...))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<objects>));
TextWriter textWriter = new StreamWriter(sync.Fs);
serializer.Serialize(textWriter, ABCObjects);
textWriter.Close();
}
}
基本问题是您的线程和窗体没有使用同一个锁对象。我不知道你是如何创建线程的,但我已经编写了一个带有 2 个任务的小控制台应用程序,这些任务将锁定并交替写入和读取文件。您必须将相同的锁定对象传递给每个任务才能使其工作。
static void Main(string[] args)
{
var lockObject = new object();
var fileName = @"C:\Users\kevin\Documents\test.txt";
Action<object> action1 = (o) =>
{
var i = 0;
while (i++ < 1000)
{
// do stuff that doesn't require the lock
lock (o)
{
Console.WriteLine("In Thread1");
// do stuff that does require the lock
var text = File.ReadAllText(fileName);
Console.WriteLine(text);
File.WriteAllText(fileName, "\tThread1");
}
}
};
// Pass in our shared lock object
Task task1 = new Task(action1, lockObject);
task1.Start();
Action<object> action2 = (o) =>
{
var i = 0;
while (i++ < 1000)
{
// do stuff that doesn't require the lock
lock (o)
{
// do stuff that does require the lock
Console.WriteLine("In Thread2");
var text = File.ReadAllText(fileName);
Console.WriteLine(text);
File.WriteAllText(fileName, "\tThread2");
}
}
};
// Pass in our shared lock object
Task task2 = new Task(action2, lockObject);
task2.Start();
// sleep main thread and let the 2 threads do their stuff
Thread.Sleep(5000);
Console.ReadLine();
}