如何在Oracle中同步线程?

如何在Oracle中同步线程?,oracle,plsql,Oracle,Plsql,我有以下程序 procedure MyProc is n number; begin -- Query A select count(*) into n from SomeTable where Column1 = 0; if n = 0 then insert into SomeTable (Column1, Column2) values (0, 'some data');

我有以下程序

    procedure MyProc is
        n number;   
    begin
        -- Query A
        select count(*) into n from SomeTable where Column1 = 0;
        if n = 0 then 
            insert into SomeTable (Column1, Column2) values (0, 'some data');
        else        
            update SomeTable set Column2 = 'some other data' where Column1 = 0;
        end if;

        commit;
    end;
此过程由多个线程中的多个作业运行:

    for i in 1..10      
    loop
            Jobname := dbms_scheduler.generate_job_name('JobName');            
            JobAction := 'begin MyProc; end;';                                                               
            dbms_scheduler.create_job(job_name => Jobname, job_type => 'PLSQL_BLOCK', job_action => JobAction, enabled => true);        
    end loop;
目标是在表SomeTable中只创建一行,所有其他作业将更新同一行

当所有作业完成时,我注意到有时会创建几行而不是一行

我知道每当执行查询A时,由于行锁,它将只看到在查询开始之前提交的表中的行。。。因此,其他一些工作没有看到变化

请问有没有办法解决这个问题

在.Net中有一个监视的概念。输入监视。退出,使所有其他线程等待资源释放

有人能帮忙吗


谢谢

您应该看看事务隔离级别。关键是哪个数据将恢复运行select计数的第一个查询。如果您没有为事务指定隔离级别,则默认情况下为readcommitted,这允许使用幻影。读取提交事务隔离级别是Oracle的默认级别。使用此设置,每个查询只能看到查询开始前提交的数据,而不能看到事务开始前提交的数据。Oracle查询不会读取脏数据或未提交的数据但是,它不会阻止其他事务修改查询读取的数据。因此,其他事务可能会在执行查询之间更改数据。任何多次执行给定查询的事务都可能经历不可重复的读取或幻影

另一个选项是可序列化的。分布式事务不支持此事务隔离级别使用可序列化事务隔离级别,查询只能访问事务开始时提交的数据以及事务本身通过插入、更新和删除所做的数据

想象一下这种情况

  • 作业1进入并执行查询。假设它得到0,因此通过if插入输入。然后插入一行
  • 作业2几乎同时进入,如果作业1尚未提交,则计数器为0
  • 作业3几乎与作业2同时进入,如果作业1尚未提交,则计数器为0


在您的特定情况下,可能有一种解决方案是使用光标进行select using select for UPDATE。它将锁定受光标影响的行。在光标关闭之前,该行不会被释放。

您可以使用dbms\u锁。大概是这样的:

procedure MyProc is
    n number;   
    h varchar2(200);
    r number;
begin
    -- Query A
    dbms_lock.allocate_unique('MyLock', h);
    r := dbms_lock.request(h, dbms_lock.x_mode); 

    select count(*) into n from SomeTable where Column1 = 0;
    if n = 0 then 
        insert into SomeTable (Column1, Column2) values (0, 'some data');
    else        
        update SomeTable set Column2 = 'some other data' where Column1 = 0;
    end if;

    commit;
    dbms_lock.release(h);
end;

@Thomas Carton,要使用DBMS_锁,dba必须将包的执行权限授予拥有该过程的用户。该过程在默认情况下是不可访问的,必须显式地授予。这正是我想要的。很好!谢谢,很好。也许我应该提到,您应该检查来自
dbms\u lock.request的返回值是否不等于0。请参阅可能返回值的文档:您的方法在一致性方面存在问题:每个查询和dml都会看到不同SCN上的数据,因此我建议您避免使用PL/SQL,只需使用bulk
merge
语句即可实现所需