脚本并发问题:脚本并发执行时出现竞争条件,导致问题

1. 使用文件锁(File Lock)

文件锁是一种简单且常用的方法,用于防止多个进程同时访问共享资源。

(1)Bash 示例

在 Bash 脚本中,可以使用 flock 命令实现文件锁:

#!/bin/bash
 
# 定义锁文件
LOCKFILE="/var/lock/my_script.lock" 
 
# 使用 flock 创建排他锁 
(
    flock -x 200
 
    # 关键代码段
    echo "脚本开始运行"
    sleep 10  # 模拟耗时操作
    echo "脚本结束运行"
 
) 200>"$LOCKFILE"
  • 说明
    • flock -x 200 表示获取一个排他锁。
    • 文件描述符 200 绑定到 $LOCKFILE,确保锁的作用范围。
    • 当脚本退出时,锁会自动释放。
(2)Python 示例

在 Python 中,可以使用 fcntl 模块实现文件锁:

import fcntl
import time
 
# 定义锁文件
lock_file = open("/var/lock/my_script.lock",  "w")
 
try:
    # 获取排他锁
    fcntl.flock(lock_file,  fcntl.LOCK_EX)
    print("脚本开始运行")
    time.sleep(10)   # 模拟耗时操作
    print("脚本结束运行")
finally:
    # 释放锁
    fcntl.flock(lock_file,  fcntl.LOCK_UN)
    lock_file.close() 

2. 使用信号量(Semaphore)

信号量是一种更高级的同步机制,适用于复杂的并发场景。

(1)Python 示例

在 Python 中,可以使用 threading.Semaphore 或 multiprocessing.Semaphore 实现信号量:

import threading
import time 
 
# 定义信号量
semaphore = threading.Semaphore(1)
 
def critical_section():
    semaphore.acquire() 
    try:
        print("进入关键代码段")
        time.sleep(5)   # 模拟耗时操作
        print("退出关键代码段")
    finally:
        semaphore.release() 
 
# 模拟并发执行
threads = [threading.Thread(target=critical_section) for _ in range(5)]
for t in threads:
    t.start() 
for t in threads:
    t.join() 

3. 使用数据库事务

如果共享资源存储在数据库中,可以通过数据库事务来避免竞争条件。

(1)SQL 示例

假设您使用的是 MySQL 数据库,可以通过事务确保操作的原子性:

START TRANSACTION;
 
-- 更新共享资源
UPDATE resource_table SET value = value + 1 WHERE id = 1;
 
COMMIT;
(2)Python 示例

结合 SQLAlchemy 使用事务:

from sqlalchemy import create_engine
from sqlalchemy.orm  import sessionmaker
 
# 创建数据库连接
engine = create_engine("mysql+pymysql://user:password@host/dbname")
Session = sessionmaker(bind=engine)
session = Session()
 
try:
    # 开始事务 
    session.begin() 
 
    # 更新共享资源
    resource = session.execute("SELECT  * FROM resource_table WHERE id = 1 FOR UPDATE").fetchone()
    session.execute("UPDATE  resource_table SET value = value + 1 WHERE id = 1")
 
    # 提交事务
    session.commit() 
except Exception as e:
    # 回滚事务
    session.rollback() 
    print(f"发生错误: {e}")
finally:
    session.close() 

4. 限制并发执行

如果不需要完全并行执行,可以通过限制并发数来避免竞争条件。

(1)Bash 示例

使用 pgrep 和 wc -l 检查当前运行的脚本实例数:

#!/bin/bash
 
# 最大并发数
MAX_CONCURRENT=1
 
# 检查当前运行的实例数
current_running=$(pgrep -fc "$(basename $0)" || true)
 
if [ "$current_running" -ge "$MAX_CONCURRENT" ]; then
    echo "已有 $current_running 个实例运行,退出"
    exit 1 
fi
 
# 脚本逻辑
echo "脚本开始运行"
sleep 10  # 模拟耗时操作
echo "脚本结束运行"
(2)Python 示例

使用 concurrent.futures 限制并发数:

from concurrent.futures  import ThreadPoolExecutor
import time
 
def task():
    print("任务开始")
    time.sleep(5)   # 模拟耗时操作
    print("任务结束")
 
# 最大并发数
MAX_WORKERS = 2
 
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
    futures = [executor.submit(task) for _ in range(5)]
    for future in futures:
        future.result() 
© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容