1

目標は、一度に 1 つのスレッド/プロセスによってのみ実行できるコードの特定のセクションを含む PHP スクリプトを作成することです。

いくつかの制限:

  • 私のシステムではセマフォが利用できません
  • マニュアルには、マルチスレッド サーバーでは flock() を使用できないと記載されているため、flock() は廃止されました。(これを確認した)

そのため、MySQL を同期に使用することは可能であると考えていましたが (なぜでしょうか?)、予期しない結果が得られています。

テストするために、スクリプト test.php で 2 つのブラウザー タブを開きます。

mysql_connect($dbhost, $dbusername, $dbpassword);
mysql_select_db($database_name);

$sql = "SELECT my_val FROM `my_table` WHERE `my_var`='status' ";
$result = mysql_query($sql)or die(mysql_error());
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "status is:".$row['my_val']."<br>\n";
mysql_free_result($result);

if ($row['my_val']=='RUNNING') die ('process is already running!');

// entering critical section

$sql = "UPDATE `my_table` SET `my_val`='RUNNING' WHERE `my_var`='status' ";
mysql_query ($sql);


sleep(10); // do some work here.
echo 'finished work<br>';

// leave critical section. let the others know
$sql = "UPDATE `my_table` SET `my_val`='NOT_RUNNING' WHERE `my_var`='status' ";
mysql_query ($sql);

表は次のとおりです。

CREATE TABLE `my_table` (
  `my_var` varchar(255) NOT NULL デフォルト '',
  `my_val` varchar(255) NOT NULL デフォルト '',
  主キー (`my_var`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `my_table`
値 (
「ステータス」、「NOT_RUNNING」
)

最初のブラウザー タブに移動し、スクリプトにアクセスします。5 秒待ってから、別のタブに移動してスクリプトにアクセスし、両方のスクリプトが並行して実行されるようにします。最初のタブが待機することを期待しており、2 番目のタブは最初のクエリの後に終了する必要があります。ただし、両方のタブが sleep() 行で待機します。これは、最初のクエリが常に 'NOT_RUNNING' を返すことを意味します。

いくつかの奇妙なこと:

  • 上記の実験を繰り返すと、両方のタブを FireFox で実行し、次に 3 番目の異なるブラウザー タイプ (たとえば Chrome) で実行すると、うまくいきます。(ステータスはスリープ中に RUNNING に設定され、ステータスが RUNNING の場合、スクリプトは早期に終了します)
  • 2 つの異なるコマンド ライン ウィンドウを使用して上記の実験を繰り返し、コマンド ラインからスクリプトを実行すると、機能します。
  • 待機中にphpMyAdminをチェックすると、ステータスが正しく更新されます。

テーブルのロック、トランザクション、SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED、SET autocommit = 1;、すべてを試しましたが、常に同じ結果が得られました。

ここで何が起こっているか知っている人はいますか?この問題の良い解決策はありますか?

テスト済みのシステム: - Win、MySQL 5.0、php 5.3 - Linux、MySQL 5.0.91-community-log、php 5.2.12

ここで完全に立ち往生しています。見てくれてありがとう!


アップデート:

回答を送信していただきありがとうございます。まだこの問題を解決できません。提案されている GET_LOCK() と session_write_close() を使用したコードを次に示します。行レベルのロック、トランザクション、およびさまざまな分離レベルも試しました。もしかして出来ないのでしょうか?

session_write_close();

mysql_connect($dbhost, $dbusername, $dbpassword);
mysql_select_db($database_name);

$sql = "CREATE TABLE IF NOT EXISTS `my_table`  (
  `my_var` varchar(255) NOT NULL default '',
  `my_val` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`my_var`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
";
mysql_query($sql)or die(mysql_error());

mysql_query ("SELECT get_lock('my_lock', 100)");

$sql = "SELECT my_val FROM `my_table` WHERE `my_var`='status' ";
$result = mysql_unbuffered_query($sql)or die(mysql_error());
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "status is:".$row['my_val']."<br>\n";
mysql_free_result($result);

if ($row['my_val']=='RUNNING') die ('process is already running!');

// entering critical section

$sql = "UPDATE `my_table` SET `my_val`='RUNNING' WHERE `my_var`='status' ";
mysql_query ($sql);

sleep(10); // do some work here.
echo 'finished work<br>';

// leave critical section. let the others know
//$sql = "UPDATE `my_table` SET `my_val`='NOT_RUNNING' WHERE `my_var`='status' ";
$sql = "REPLACE INTO `my_table` (`my_var`,`my_val`) VALUES ('status', 'NOT_RUNNING')";

mysql_query ($sql);
$result = mysql_query($sql)or die(mysql_error());


mysql_query ("SELECT release_lock('my_lock')");

die();
4

2 に答える 2

2

2 番目のブラウザー ウィンドウは、sleepコマンドでハングしていません。セッションがブロックされているため、ブロックされています (したがって、同じセッションを開こうとしています)。現在のセッションが不要になった場合 (つまり、ブロックしたくない場合) は、session_write_close...で閉じることができます。

于 2010-08-24T17:12:09.773 に答える
0

Mysql にはGET_LOCKという関数があります。

于 2010-08-24T17:43:41.987 に答える