单例模式与方法的同步异步
项目功能需求:
添加一个log记录工具类,将每天的log信息分别记入新建的文本文件中。(要求自己写一个简单实现,不借助第三方类库)。
首先想法:应log工具类需要访问文本资源,将其设计为单例模式,以避免多线程访问同一资源报异常。
public class TextLogger
{
private static string _logPath = Path.Combine(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, "Logs\\Log\\");
private static TextLogger _instance = null;
public static TextLogger Instance
{
get
{
if (_instance == null)
{
_instance = new TextLogger();
}
return _instance;
}
}
private TextLogger()
{
if (!Directory.Exists(_logPath))
{
Directory.CreateDirectory(_logPath);
}
}
public void WriteLog(string message)
{
try
{
using (StreamWriter sw = new StreamWriter(_logPath + DateTime.Today.ToString("yyyy-MM-dd") + ".log", true))
{
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fffff") + "\t" + message);
}
}
catch
{
}
}
}
使用TextLogger工具,TextLogger.Instance.WriteLog("Application_Start Begin");
,在单线程下可以正常工作,但到了多线程下会偶然报bug,该怎么解决呀?
理清概念:
- 单例模式:保证的是在多线程下,该类只存在唯一的对象(不能保证,对象的某个方法之被单线程调用)
- 方法的同步异步:从概念上理解同步与异步与是否多线程没有关系,但在.net,异步的实现本质上是:执行耗时操作时,为消除当前线程的阻塞,开启新的线程执行其他任务。(所以在方法中保证同步执行需要加锁lock)
举个测试小栗子,在控制中新建
class SyncHelper
{
public void Execute()
{
Console.WriteLine("Excute at {0}", DateTime.Now);
Thread.Sleep(5000);
}
}
Main函数
class Program
{
static void Main(string[] args)
{
SyncHelper helper = new SyncHelper();
Timer timer = new Timer(
delegate
{
helper.Execute();
}, null, 0, 1000);
Console.Read();
}
}
执行发现,每个1s打印异常而非5s。在Execute()
方法上添加Attribute
线程锁[MethodImpl(MethodImplOptions.Synchronized)]
在执行发现输出将为5s。
[MethodImplAttribute(MethodImplOptions.Synchronized)]
仍然采用加锁的机制实现线程的同步。- 如果
[MethodImplAttribute(MethodImplOptions.Synchronized)]
被应用到instance method,相当于对当前实例加锁。 - 如果
[MethodImplAttribute(MethodImplOptions.Synchronized)]
被应用到static method,相当于当前类型加锁
结论
所以最简单的解决方法:在TextLogger.WriteLog
方法上添加[MethodImpl(MethodImplOptions.Synchronized)]
参考自[MethodImpl(MethodImplOptions.Synchronized)]、lock(this)与lock(typeof(...))