怎么样能多线程,获取一个全局唯一的值。

Shalves 2011-03-18 02:24:59
我现在要多线程方式访问一个全局的值,这个值必需是全局唯一的。(假设是 int类型)
从0开始以1步进递增。

就是说,不管哪个线程进来,得到的这个值,都别跟别的线程得到的不一样。
...全文
224 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
Shalves 2011-03-22
  • 打赏
  • 举报
回复
先感谢各位的指导!
先发上我的解决办法,如果有问题请回帖指出来,三天后结帖:

/// <summary>
/// 稿件编号驱动工厂
/// <para>稳定</para>
/// </summary>
public static class NumberProviderFactory
{
/// <summary>
/// 全局稿件编号驱动
/// </summary>
private static INumberProvider GlobalNumberProvider;

/// <summary>
/// 获取一个INumberProvider实例
/// <para>单例模式,全局唯一</para>
/// </summary>
/// <param name="configFilePath">文档配置文件路径</param>
/// <returns></returns>
public static INumberProvider GetNumberProvider(string configFilePath)
{
if (GlobalNumberProvider == null)
{
GlobalNumberProvider = new NumberProvider(configFilePath);
}
return GlobalNumberProvider;
}
}

/// <summary>
/// 稿件编号驱动接口
/// </summary>
public interface INumberProvider
{
/// <summary>
/// 访问线程计数器
/// </summary>
int VisitorCounter { get; }

/// <summary>
/// 获取当前串号
/// </summary>
/// <returns></returns>
int GetCurrent();

/// <summary>
/// 重置当前串号为0
/// </summary>
void Reset();
}

/// <summary>
/// 稿件编号驱动
/// </summary>
public class NumberProvider : INumberProvider
{
/// <summary>
/// xml配置文件的路径
/// </summary>
private string xmlPath = string.Empty;

/// <summary>
/// 单线程排它锁
/// </summary>
private Mutex AppLocker = new Mutex();

private int _VisitorCounter;
/// <summary>
/// 访问线程计数器
/// </summary>
public int VisitorCounter
{
get { return this._VisitorCounter; }
}

#region private int CurrentNumber { get; set; }
/// <summary>
/// 获取或设置当前串号
/// </summary>
private int CurrentNumber
{
get
{
try
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlPath);
XmlNode xn = xd.SelectSingleNode("/document/numberseed/value");
if (xn != null)
return int.Parse(xn.InnerText);
return 0;
}
catch
{
return -1;
}
}
set
{
try
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlPath);

XmlNode xn = xd.SelectSingleNode("/document/numberseed/value");
if (xn != null)
xn.InnerText = value.ToString();

xd.Save(xmlPath);
}
catch { }
}
}
#endregion

/// <summary>
/// 使用当前应用程序指定的配置文件计数
/// </summary>
/// <param name="configFilePath">文档配置文件路径</param>
public NumberProvider(string configFilePath)
{
if (string.IsNullOrEmpty(configFilePath))
this.xmlPath = HttpRuntime.AppDomainAppPath + "documentconf.xml";
else
this.xmlPath = configFilePath;

this._VisitorCounter = 0;
}

#region public virtual int GetCurrent()
/// <summary>
/// 获取当前串号
/// </summary>
/// <returns></returns>
public virtual int GetCurrent()
{
this._VisitorCounter++;
AppLocker.WaitOne();

try
{
CurrentNumber++;
return CurrentNumber;
}
finally
{
AppLocker.ReleaseMutex();
this._VisitorCounter--;
}
}
#endregion

#region public virtual void Reset()
/// <summary>
/// 重置当前串号为0,并更新统计日期
/// </summary>
public virtual void Reset()
{
this._VisitorCounter++;
AppLocker.WaitOne();

try
{
CurrentNumber = 0;
XmlDocument xd = new XmlDocument();
xd.Load(xmlPath);
XmlNode xn = xd.SelectSingleNode("/document/numberseed/date");
if (xn != null)
xn.InnerText = DateTime.Now.Date.ToString("yyyy-MM-dd");
xd.Save(xmlPath);
}
finally
{
AppLocker.ReleaseMutex();
this._VisitorCounter--;
}
}
#endregion
}

public class Global : System.Web.HttpApplication
{
/// <summary>
/// 稿件编号控制器
/// </summary>
Timer NumberControler;

/// <summary>
/// 控制器同步句柄
/// </summary>
AutoResetEvent NumberContorlerHandle = new AutoResetEvent(false);

protected void Application_Start(object sender, EventArgs e)
{
DateTime nextDayStart = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.AddDays(1).Day, 0, 0, 0);

//当前与下一天开始时间的时间差
TimeSpan diff = nextDayStart - DateTime.Now;

TimerCallback tc = new TimerCallback(ResetNumberCount);
NumberControler = new Timer(tc, NumberContorlerHandle, diff, new TimeSpan(24, 0, 0));
}

/// <summary>
/// 初始化稿件编号
/// </summary>
/// <param name="o"></param>
protected void ResetNumberCount(object o)
{
AutoResetEvent are = (AutoResetEvent)o;
NumberProviderFactory.GetNumberProvider(HttpRuntime.AppDomainAppPath + "documentconf.xml").Reset();
are.Set();
}
}

lizhibin11 2011-03-18
  • 打赏
  • 举报
回复
先说第一个,实际interlocked是可以的,字符串加上那个被原子操作的整数就行了。当然用Lock也绝对没问题,这个过程并不耗时。
第二个,如果一个访问者的请求还没有完成,其它的进来了,会等待,直到锁被释放后进去一个,其余的继续等待,直到全部完成,这是正常的,你什么都不用做。
vrhero 2011-03-18
  • 打赏
  • 举报
回复
你没去看那个链接吗...如果同步对象很复杂可以用同步事件AutoResetEvent或ManualResetEvent...

但是Web请求是无连接的,等待时间太长会导致超时...你可以用Ajax异步请求或者先理清楚逻辑改变操作,尽量不要用同步模式...
Shalves 2011-03-18
  • 打赏
  • 举报
回复
其实并不是在加这个全局唯一的值的过程需要时间,是去数据库中获取这个值的过程需要时间。而且加完之后,还要写入数据库,以保证下次获取的是上次加过的值。
Shalves 2011-03-18
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 lizhibin11 的回复:]
你最后这个问题实际是个复杂的问题了,如果一个过程在一秒以上,那么lock整个过程肯定是思路上有问题了,要分析这个过程中哪些变量会有线程同步问题,然后分步lock。具体问题具体分析。
[/Quote]

我其实是想找一个比较完善的解决办法的,既然这样,那我说实际的吧。

需求:我现在要生成一个编号这个编号的格式是“特定的Id号_年月日_一个全局唯一的编号”,这个编号要每天0点0分0时0秒初始化成0,并且,同一天里,不同特定Id号等到的全局唯一编号不能相同。

这个编号的请求,是Asp.Net页面完成的。这个页面的访问量可能在同一时间产生很多个申请这个编号的进程。
初始化的问题,我可以用SqlServer的定时“事务”来解决。就是将这个值存到一个表的字段里,每天定时设置成0。其实这样也不保险,如果那个时间正好有客户请求呢?但我目前也只想到这个办法。

另外一个重点问题,如果一个访问者的请求还没有完成,其它N个访问者的请求就已经来了,这个时候我怎么办呢?
lizhibin11 2011-03-18
  • 打赏
  • 举报
回复
你最后这个问题实际是个复杂的问题了,如果一个过程在一秒以上,那么lock整个过程肯定是思路上有问题了,要分析这个过程中哪些变量会有线程同步问题,然后分步lock。具体问题具体分析。
Shalves 2011-03-18
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 lizhibin11 的回复:]
你那个整数的操作只是简单地加一,lock又不会耗费很长时间,基本上客户端请求应答没什么影响。……
[/Quote]

我只是举个例子嘛,要真是的话,我全一个全局静态的变量就行了,反正加一下也不花多长时间;

我的意思是说“如果这个值生成的过程需要一定的时间,比如10秒种,而在上一个线程进行的时候,另一个线程进来了,我怎么让它等会儿?如果这个线程是Asp.net Request产生的,这个时候,我该怎么回应这个请求呢?还是不用回应?直接让线程等待就行了?”
lizhibin11 2011-03-18
  • 打赏
  • 举报
回复
那你的字母组合生成的规律是什么?如果没有规律,用lock就行了,我也是做东西做的神经了,习惯性的避免lock,其实在绝大部分程序中,lock和interlocked的差别根本不会带来任何影响。
Shalves 2011-03-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 lizhibin11 的回复:]
Interlocked.Increment(ref i);
[/Quote]

我刚试了,这个好像可以;但如果我要获取的值不是int类型呢?

可能是字母组合A~Z{n}
wangji666666 2011-03-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 lizhibin11 的回复:]
Interlocked.Increment(ref i);
[/Quote]
既然是多线程,并且已经将值更改了,不存在死等的问题
lizhibin11 2011-03-18
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 shalves 的回复:]
引用 2 楼 vrhero 的回复:
线程同步...最简单的就是用lock加锁...

去看MSDN...

线程同步(C# 编程指南)


加了锁,别的线程进来,如果是一个Asp.Net程序的一个客户端请求呢?我怎么回应,或者设置线程等待?
[/Quote]
你那个整数的操作只是简单地加一,lock又不会耗费很长时间,基本上客户端请求应答没什么影响。
热情的菜鸟 2011-03-18
  • 打赏
  • 举报
回复
加锁只能死等
lizhibin11 2011-03-18
  • 打赏
  • 举报
回复
我解释一下,Interlocked类的方法都是原子操作,和普通的lock等锁机制相比,Interlocked是锁定内存总线,保证了过程的完整性。不是先赋值,再计算,再赋值。效率比lock提高2倍左右。
Shalves 2011-03-18
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 vrhero 的回复:]
线程同步...最简单的就是用lock加锁...

去看MSDN...

线程同步(C# 编程指南)
[/Quote]

加了锁,别的线程进来,如果是一个Asp.Net程序的一个客户端请求呢?我怎么回应,或者设置线程等待?
vrhero 2011-03-18
  • 打赏
  • 举报
回复
线程同步...最简单的就是用lock加锁...

去看MSDN...

线程同步(C# 编程指南)
lizhibin11 2011-03-18
  • 打赏
  • 举报
回复
Interlocked.Increment(ref i);

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧