php 并发加锁示例

大家一定会遇到当多个人同时请求一个方法时,如果涉及到金额的计算,就会出现误差,需要当一个用户在操作的时候对其方法进行加锁,减小误差,但是当5000个人同时访问时,就会出现很多用户在等待的情况,所以需要慎用。下面是对加锁的一个案例:

1,LockSystem.php文件内容

<?php

class LockSystem
{
    const LOCK_TYPE_DB = 'SQLLock';
    const LOCK_TYPE_FILE = 'FileLock';
    const LOCK_TYPE_MEMCACHE = 'MemcacheLock';

    private $_lock = null;
    private static $_supportLocks = array('FileLock', 'SQLLock', 'MemcacheLock');

    public function __construct($type, $options = array())
    {
        if (false == empty($type)) {
            $this->createLock($type, $options);
}
    }

    public function createLock($type, $options = array())
    {
        if (false == in_array($type, self::$_supportLocks)) {
            throw new Exception("not support lock of ${type}");die;
        }
        //$type = "Common\\Tool\\AgLock\\".$type;
        $this->_lock = new $type($options);
    }

    public function getLock($key, $timeout = ILock::EXPIRE)
    {
        if (false == $this->_lock instanceof ILock) {
            throw new Exception('false == $this->_lock instanceof ILock');
        }
        $this->_lock->getLock($key, $timeout);
    }

    public function releaseLock($key)
    {
        if (false == $this->_lock instanceof ILock) {
            throw new Exception('false == $this->_lock instanceof ILock');
        }
        $this->_lock->releaseLock($key);
    }
}

interface ILock
{
    const EXPIRE = 5;

    public function getLock($key, $timeout = self::EXPIRE);

    public function releaseLock($key);
}

class FileLock implements ILock
{
    private $_fp;
    private $_single;

    public function __construct($options)
    {
        if (isset($options['path']) && is_dir($options['path'])) {
            $this->_lockPath = $options['path'] . '/';
        } else {
            $this->_lockPath = '';
        }

        $this->_single = isset($options['single']) ? $options['single'] : false;
    }

    public function getLock($key, $timeout = self::EXPIRE)
    {
//        $startTime = Timer::getTimeStamp();

        $file = md5(__FILE__ . $key);
        $this->fp = fopen($this->_lockPath . $file . '.lock', "w+");
        if (true || $this->_single) {
            $op = LOCK_EX + LOCK_NB;
        } else {
            $op = LOCK_EX;
        }
        if (false == flock($this->fp, $op, $a)) {
            throw new Exception('failed');
        }

        return true;
    }

    public function releaseLock($key)
    {
        flock($this->fp, LOCK_UN);
        fclose($this->fp);
    }
}

class SQLLock implements ILock
{
    public function __construct($options)
    {
        $this->_db = new mysql();
    }

    public function getLock($key, $timeout = self::EXPIRE)
    {
        $sql = "SELECT GET_LOCK('" . $key . "', '" . $timeout . "')";
        $res = $this->_db->query($sql);
        return $res;
    }

    public function releaseLock($key)
    {
        $sql = "SELECT RELEASE_LOCK('" . $key . "')";
        return $this->_db->query($sql);
    }
}

//class MemcacheLock implements ILock
//{
//    public function __construct($options)
//    {
//
//        $this->memcache = new Memcache();
//    }
//
//    public function getLock($key, $timeout = self::EXPIRE)
//    {
//        $waitime = 20000;
//        $totalWaitime = 0;
//        $time = $timeout * 1000000;
//        while ($totalWaitime < $time && false == $this->memcache->add($key, 1, $timeout)) {
//            usleep($waitime);
//            $totalWaitime += $waitime;
//        }
//        if ($totalWaitime >= $time)
//            throw new Exception('can not get lock for waiting ' . $timeout . 's.');
//
//    }
//
//    public function releaseLock($key)
//    {
//        $this->memcache->delete($key);
//    }
//}

2、locktest.php 文件

<?php
include_once('./LockSystem.php');
$result=pay(1,100);
var_dump($result);
function pay($userId, $money)
{

    $mysql=new mysqli("127.0.0.1",'root','root','task',3306);

    if (false == is_int($userId) || false == is_int($money)) {
        return false;
    }

    try {
        //创建锁
        $lockSystem = new LockSystem(LockSystem::LOCK_TYPE_FILE);
        //获取锁
        $lockKey = 'pay' . $userId;
        $lockSystem->getLock($lockKey, 8);

  //取出总额
  $total = getUserLeftMoney($userId,$mysql);
  //花费大于剩余
  if ($money > $total) {
      $ret = false;
  } else {
      //余额
      $left = intval($total) - intval($money);

      //更新余额
      $ret = setUserLeftMoney($userId, $left,$mysql);
  }
  //释放锁
  $lockSystem->releaseLock($lockKey);
  echo "完成";
 } catch (Exception $e) {
        //释放锁
        $lockSystem->releaseLock($lockKey);
    }

}

//取出用户的余额
function getUserLeftMoney($userId,$mysql)
{
    if (false == is_int($userId)) {
        return 0;
    }

    $sql = "select account from user_account where userid = ${userId}";
    $result=$mysql->query($sql);
    while($s=$result->fetch_assoc()){
        $b=$s['account'];
    }
    return $b;
}

//更新用户余额
function setUserLeftMoney($userId, $money,$mysql)
{
    if (false == is_int($userId) || false == is_int($money)) {
        return false;
    }

    $sql = "update user_account set account = ${money} where userid = ${userId}";

    //$mysql = new mysql();//mysql数据库
    return $mysql->query($sql);
}

?>

希望对大家有帮助,但是我还是喜欢将3中方式写成3个文件使用

原文地址:https://www.jb51.net/article/94878.htm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值