如果在程序中需要创建全局唯一的序号,那么必须对创建序号的过程进行同步处理,防止多个并发访问时出现相同序号的情况。下面列出几种方法供大家参考。
利用数据库的方法
后面的例子都基于ms sql server,如果使用oracle可以直接读取sequence对象,则不需要进行如此复杂的操作。
方法1:利用表锁定
表结构:
create table xtab (seq_id int primary key, create_time datetime)
存储过程或sql语句:
begin tran
declare @max_seq int
–读出记录时锁定表
select @max_seq=max(seq_id) from xtab with (tablockx)
set @max_seq = isnull(@max_seq,0)
set @max_seq = @max_seq+1
print @max_seq
insert into xtab values(@max_seq,getdate())
commit
–变量@max_seq 中存放的就是当前产生的序号
方法2:利用自增字段
如果利用自增变量可以通过方法1中锁定表,然后再插入记录,并读取最大值的方法。不过下面讲的是通过c#的ado.net来插入记录,并读取已经插入的记录的自增字段。
表结构:
create table xtab_i (seq_id int identity(1,1) primary key, create_time datetime)
c#代码,利用时间处理函数在数据被更新时同时读取@@identity变量的值。完整内容参考:oledbwrap.cs 文件。
//参数内容:
//szselectsql = ”select * from xtab_i where seq_id isnull”;
//sztabname = “xtab_i”;
/// <summary>
/// 通过查询语句 创建dataset对象,返回的对象用于执行插入操作
/// </summary>
/// <param name=”szselectsql”>sql语句</param>
/// <param name=”sztabname”>表名称</param>
/// <returns>用于插入的dataset对象</returns>
public dataset createinsertdataset_byselect(string szselectsql,string sztabname){
m_dbadapter.selectcommand = new oledbcommand(szselectsql, m_dbconn);
oledbcommandbuilder cb = new oledbcommandbuilder(m_dbadapter);
dataset ds = new dataset();
m_dbadapter.fill(ds, sztabname);
m_dbadapter.rowupdated += new oledbrowupdatedeventhandler(onrowupdated_inserted);
return ds;
}
//—————-私有事件处理
/// <summary>
/// 处理数据插入的更新事件
/// </summary>
/// <param name=”sender”></param>
/// <param name=”args”></param>
protected void onrowupdated_inserted(object sender, oledbrowupdatedeventargs args)
{
oledbcommand idcmd = new oledbcommand(“select @@identity”, m_dbconn);
if (args.statementtype == statementtype.insert)
{
object robj = idcmd.executescalar();
if(robj == null) m_idbidentity =-1;
else if( robj.tostring() !=””)
m_idbidentity = int32.parse(robj.tostring());
else
m_idbidentity =-1;
}
idcmd.dispose();
//m_idbidentity变量中包含的就是当前插入列的自增字段的值
}
利用程序控制
方法1:利用asp.net中的application对象
利用asp.net中的全局httpapplicationstate对象。
c#代码:
application.lock();
application[“xlock”]=”locked’;
try
{ //必须捕捉异常,避免无法解锁对象
//your code here
}
catch (exception e)
{
}
application[“xlock”]=”unlock”;
方法2:利用操作系统中的同步对象来实现同步
在dotnet的框架的system.threading命名空间下定义了多种用于同步的类,例如:mutex,semaphore。下面介绍一下利用互斥对象mutex来实现同步的方法。
c#代码:
void test()
{ //需要引入 system.threading;
//创建名为mymutex的互斥对象,如果os已经创建过同名对象则只是重新获得句柄
mutex m = new mutex(false, “mymutex”);
//在10秒内等待取得访问权
boolean getmutex=m.waitone(10*1000,false);
if(getmutex)
{ //已经取得访问权
try
{
//必须捕捉异常,避免无法解锁对象
//your code here
}
catch(exception e)
{
}
m.releasemutex();
}
else
{
//没有取得访问权
}
}
在程序中应该尽量使用mutex这类可以命名的同步对象以达到创建多个同步对象,对多种资源进行同步的目的。如果要实现多个同入可以使用信号量 semaphore。
其他方法:利用lock关键字防止线程并发同一段代码执行
c#代码:
class ctest
{
int balance;
void increase()
{
lock (this)
{
balance++;
}
}
}
由于asp.net程序在iis中可能配置不同的线程模式,所以在asp.net程序尽可能不要使用这种方法同步线程,而在普通的应用程序中是可以使用这种方法。
附:oledbwrap.cs ,是一个很简单的封装类,封装了基本的数据库操作。
using system;
using system.data;
using system.data.oledb;
namespace wyy.wrap
{
/// <summary>
/// wdbwrap 说明:完成数据库访问功能
/// 1 创建connection,adapter,管理对象内的conn和adapter对象
/// 2 通过select结果集填充dataset
/// 3 插入
/// 4 更新
/// </summary>
public class oledbwrap
{
//——–公共方法
/// <summary>
/// 在对象被清除时会自动清除 数据库连接对象
/// </summary>
public oledbwrap()
{
m_dbconn = new oledbconnection(dbstring);
m_fautodelconn = true;
m_dbadapter = new oledbdataadapter();
m_dbconn.open();
}
/// <summary>
/// 通过连接字符串构造内部的数据库连接对象
/// </summary>
/// <param name=”strconnection”>ado连接字符串</param>
public oledbwrap(string strconnection)
{
m_dbconn = new oledbconnection(strconnection);
m_fautodelconn = true;
m_dbadapter = new oledbdataadapter();
m_dbconn.open();
}
/// <summary>
/// 通过现有连接构造对象,在对象被清除时不会自动清除 数据库连接对象
/// </summary>
/// <param name=”conn”>现存的数据库连接对象</param>
public oledbwrap(oledbconnection conn)
{
m_dbconn = conn;
m_fautodelconn = false;
m_dbadapter = new oledbdataadapter();
//m_dbconn.open();
}
public virtual void dispose()
{
m_dbadapter.dispose();
if(m_fautodelconn)
{
m_dbconn.close();
m_dbconn.dispose();
}
}
/// <summary>
/// 通过sql语句创建datareader对象
/// </summary>
/// <param name=”szsql”>sql语句</param>
/// <returns>datareader对象</returns>
public oledbdatareader createdatareader(string szsql)
{
oledbcommand cmd = new oledbcommand(szsql,m_dbconn);
oledbdatareader dr= cmd.executereader();
cmd.dispose();
return dr;
}
/// <summary>
/// 通过sql查询语句,返回第一行结果,可以用于执行类似与select count(*)的语句
/// </summary>
/// <param name=”szsql”>sql语句</param>
/// <returns>返回对象</returns>
public object executescalar(string szsql)
{
oledbcommand idcmd = new oledbcommand(szsql, m_dbconn);
object robj = idcmd.executescalar();
idcmd.dispose();
return robj;
}
/// <summary>
/// 调用oledbcommand 的 executenonquery
/// </summary>
/// <param name=”szsql”></param>
/// <returns></returns>
public int executenonquery(string szsql)
{
oledbcommand idcmd = new oledbcommand(szsql, m_dbconn);
int iret = idcmd.executenonquery();
idcmd.dispose();
return iret;
}
/// <summary>
/// 创建查询用dataset对象
/// </summary>
/// <param name=”szsql”>查询sql语句</param>
/// <param name=”sztabname”>表名称</param>
/// <returns>已经被填充的dataset对象</returns>
public dataset createselectdataset(string szsql,string sztabname)
{
m_dbadapter.selectcommand = new oledbcommand(szsql,m_dbconn);
dataset ds = new dataset();
m_dbadapter.fill(ds,sztabname);
return ds;
}
/// <summary>
/// 通过查询语句 创建dataset对象,返回的对象用于执行插入操作
/// </summary>
/// <param name=”szselectsql”>sql语句</param>
/// <param name=”sztabname”>表名称</param>
/// <returns>用于插入的dataset对象</returns>
public dataset createinsertdataset_byselect(string szselectsql,string sztabname)
{
m_dbadapter.selectcommand = new oledbcommand(szselectsql, m_dbconn);
oledbcommandbuilder cb = new oledbcommandbuilder(m_dbadapter);
dataset ds = new dataset();
m_dbadapter.fill(ds, sztabname);
m_dbadapter.rowupdated += new oledbrowupdatedeventhandler(onrowupdated_inserted);
return ds;
}
/// <summary>
/// 通过查询语句 创建dataset对象,返回的对象用于执行更新操作
/// </summary>
/// <param name=”szselectsql”>sql语句</param>
/// <param name=”sztabname”>表名称</param>
/// <returns>用于更新的dataset对象</returns>
public dataset createupdatedataset_byselect(string szselectsql,string sztabname)
{
m_dbadapter.selectcommand = new oledbcommand(szselectsql, m_dbconn);
oledbcommandbuilder cb = new oledbcommandbuilder(m_dbadapter);
dataset ds = new dataset();
m_dbadapter.fill(ds, sztabname);
return ds;
//m_dbadapter.rowupdated += new oledbrowupdatedeventhandler(onrowupdated_update);
}
//—————-私有事件处理
/// <summary>
/// 处理数据插入的更新事件
/// </summary>
/// <param name=”sender”></param>
/// <param name=”args”></param>
protected void onrowupdated_inserted(object sender, oledbrowupdatedeventargs args)
{
oledbcommand idcmd = new oledbcommand(“select @@identity”, m_dbconn);
if (args.statementtype == statementtype.insert)
{
object robj = idcmd.executescalar();
if(robj == null)
m_idbidentity =-1;
else if( robj.tostring() !=””)
m_idbidentity = int32.parse(robj.tostring());
else
m_idbidentity =-1;
}
idcmd.dispose();
}
//————公共属性
/// <summary>
/// 在插入数据后获取新数据行中自增字段的值,目前只能支持一个自增字段
/// </summary>
public int32 dbidentity {get{return m_idbidentity;} }
/// <summary>
/// 数据库连接字符串,保存在web.config文件中 <appsettings>节
/// </summary>
public string dbstring {get{return system.configuration.configurationsettings.appsettings[“dbstr”];}}
//————公共变量
/// <summary>
/// 数据库连接
/// </summary>
public oledbconnection m_dbconn;
/// <summary>
/// 查询adapter
/// </summary>
public oledbdataadapter m_dbadapter;
public const string m_szrooturl =”/copathway/todo/”;
//———- 私有变量
/// <summary>
/// 保存数据库插入是自增字段的值
/// </summary>
protected int32 m_idbidentity =-1;
protected bool m_fautodelconn = true;
}
}