关于多线程锁:锁代码还是锁资源?

p
philphy
楼主 (未名空间)
有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?

举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
txt.

现在我要创建多个线程,去往文件存东西。举例子如下(C++)。

mutex mx;

void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
f.close();
}

这段代码看似没有问题,但是却有个缺陷:锁的是代码,并非资源。换句话说,在多线程下,任何
一个线程都会被另外一个线程block住,因为这里只要有两个线程同时碰到这个锁,就
会出现锁问题。
这时的多线程毫无意义了,因为这个函数其实无法并行。

可问题是,我往一个文件1.txt里面写东西,不应该影响另外一个线程往2.txt里面写东西。

所以是不是应该锁资源,而不是锁代码?

如果是这样就应该这么写

//把文件名和mutex创建map,每个文件名都有一个独立的mutex
map mx_map;

void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx_map[filename]);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
f.close();
}

这样,锁就是对应于某一个文件。多个线程的锁并不一定一致,也就不一定冲突。

请问诸位高人,我说的有道理么?
A
AlphaCode
2 楼
不存在锁代码,锁的都是资源

【 在 philphy (海天之间) 的大作中提到: 】
: 有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
: 举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
: txt.
: 现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
: mutex mx;
: void write_to_file(string filename, string binary_data)
: {
: unique_lock lk(mx);
: fstream f(filename);
: f.write(&(binary_data[0]), binary_data.size());
: ...................
p
philphy
3 楼
上面的第一段代码,锁的是代码,因为任何一个线程到这里就会与其他线程冲突。
整个函数全部被锁住了。

【 在 AlphaCode (Alpha) 的大作中提到: 】
: 不存在锁代码,锁的都是资源
A
AlphaCode
4 楼
想想你为何要加那个锁

【 在 philphy (海天之间) 的大作中提到: 】
: 上面的第一段代码,锁的是代码,因为任何一个线程到这里就会与其他线程冲突。
: 整个函数全部被锁住了。
c
convergence
5 楼
看来c++的多线程,比java的简单多了。

【 在 philphy (海天之间) 的大作中提到: 】
: 有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
: 举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
: txt.
: 现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
: mutex mx;
: void write_to_file(string filename, string binary_data)
: {
: unique_lock lk(mx);
: fstream f(filename);
: f.write(&(binary_data[0]), binary_data.size());
: ...................
p
philphy
6 楼
because another thread may write to the same file

【 在 AlphaCode (Alpha) 的大作中提到: 】
: 想想你为何要加那个锁
c
convergence
7 楼
i/o是最慢的地方,比如,数据库备份,一般也就一天一次。你这样频繁地写入文件,
会让系统变得很慢。

【 在 philphy (海天之间) 的大作中提到: 】
: because another thread may write to the same file
c
cxu123
8 楼
你这是画蛇添足,文件写操作,生成文件句柄的时候,操作系统可以加锁,其它人试图打开文件写会失败。
c
convergence
9 楼
right

【 在 cxu123 (NY) 的大作中提到: 】
: 你这是画蛇添足,文件写操作,生成文件句柄的时候,操作系统可以加锁,其它人试图
: 打开文件写会失败。
d
digua
10 楼
是的。不同的进程,打开不同的文件,本可以不上锁的(看楼主说的应该是这样)。现在楼主的写法,一个线程(共享内存的多线程)用write_to_file()打开文件1, 另一个线程就不能用write_to_file()打开文件2了。

【 在 cxu123 (NY) 的大作中提到: 】
: 你这是画蛇添足,文件写操作,生成文件句柄的时候,操作系统可以加锁,其它人试图
: 打开文件写会失败。
d
digua
11 楼
这样的话,一般有两个提高性能的方案:一是把这个文件拆解成小文件,二是用数据库代替文件。两种做法都是为了提高I/O的并行度。

【 在 philphy (海天之间) 的大作中提到: 】
because another thread may write to the same file
c
cxu123
12 楼
是啊,楼主不能埋头写程序,不管背景知识。

操作系统提供的资源。这些如果是竞争的,基本都有锁,你不用自己搞一个。

你只要使用这些资源和处理竞争资源失败的情况就行了。

【 在 digua (姚之FAN) 的大作中提到: 】
是的。不同的进程,打开不同的文件,本可以不上锁的(看楼主说的应该是这样)。现
在楼主的写法,一个线程(共享内存的多线程)用write_to_file()打开文件1, 另一个
线程就不能用write_to_file()打开文件2了。
h
heteroclinic
13 楼
锁就是双boolean,在 cpu 架构下确定atomic mutation.不存在什么资源锁,锁本身就
是一段代码。
w
wdong
14 楼
depend on你的文件大小, 以及你是SSD还是HDD, 你这么写还真有可能是有
奇效. 如果你的binary_data很大, 有几G以上, 你的线程数很多, 而所有的
I/O都去同一个传统HDD, 那么这么写应该是效率最高的.

我经常写这种代码, 可以最大化磁盘吞吐量. mutex保护的是让磁头不要
乱动. 不过SSD的话就么写就没啥好处了. 这个和你问的没啥关系, 但是
事情的复杂性往往超出人的想象.

【 在 philphy (海天之间) 的大作中提到: 】
有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
txt.
现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
mutex mx;
void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
...................
d
digua
15 楼
楼主似乎修改了原文?

你这样写代码,相当于把1.txt到100.txt,合起来共用一个锁。一个线程访问1.txt,
另一个线程就不能访问任何一个其它的文件。

如果因为某种原因你要用mutex,那应该用100个mutex,每个文件给一个。

【 在 philphy (海天之间) 的大作中提到: 】
有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
txt.
现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
mutex mx;
void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
...................
p
philphy
16 楼
其实我只是举一个例子,举的不好请原谅
我想说的,是unique_lock其实锁住的,是一段代码,而不是锁住一个资源

如果要锁住一个资源,就必须给这个资源一个独一无二的mutex

【 在 philphy (海天之间) 的大作中提到: 】
有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
txt.
现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
mutex mx;
void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
...................
p
philphy
17 楼
万一不同的进程打开同一个文件呢?
所以我是给每一个文件上一个锁
我觉得这就是atomic的一个例子吧
【 在 digua (姚之FAN) 的大作中提到: 】
是的。不同的进程,打开不同的文件,本可以不上锁的(看楼主说的应该是这样)。现
在楼主的写法,一个线程(共享内存的多线程)用write_to_file()打开文件1, 另一个
线程就不能用write_to_file()打开文件2了。
l
littlebirds
18 楼
锁的粒度问题,太细太粗都不好。
c
cxu123
19 楼
你这个问题的答案,还是和操纵系统有关。

线程(进程)最大的开销,就是线程(进程)切换,也就是一个线程,如果拿不到锁,就会block,然后被操作系统切换走,这个上下文切换开销,如果线程多,就很高。有
的操纵系统是按照进程调度,有的是按照线程调度。

所以你的资源如果要锁,尽可能让程序能快速访问,减少block的几率。所以在锁里放
文件操纵或者类似运行速度慢的东西,是不明智的。除非没啥对性能的要求

【 在 philphy (海天之间) 的大作中提到: 】
其实我只是举一个例子,举的不好请原谅
我想说的,是unique_lock其实锁住的,是一段代码,而不是锁住一个资源
如果要锁住一个资源,就必须给这个资源一个独一无二的mutex
p
philphy
20 楼
跑题了,你说的与线程毫无相关吧

【 在 convergence (Rex) 的大作中提到: 】
i/o是最慢的地方,比如,数据库备份,一般也就一天一次。你这样频繁地写入文件,
会让系统变得很慢。
p
philphy
21 楼
多谢,这是我看到的唯一一个切题的答复!
【 在 digua (姚之FAN) 的大作中提到: 】
楼主似乎修改了原文?
你这样写代码,相当于把1.txt到100.txt,合起来共用一个锁。一个线程访问1.txt,
另一个线程就不能访问任何一个其它的文件。
如果因为某种原因你要用mutex,那应该用100个mutex,每个文件给一个。
d
digua
22 楼
不用客气。:) 改成用mutex array就好了。

【 在 philphy (海天之间) 的大作中提到: 】
多谢,这是我看到的唯一一个切题的答复!
c
cxu123
23 楼
原来你想知道的是这个,这是常识性问题。

当你创建一个新文件的时候,想想操作系统内部怎么锁文件的,每个文件一个锁。1万
个文件同时写,就有一万个锁

【 在 philphy (海天之间) 的大作中提到: 】
多谢,这是我看到的唯一一个切题的答复!
A
AlphaCode
24 楼
你自己概念混乱而已。代码和资源怎么混为一谈?再说一遍,你锁的是资源,锁代码是个什么东西?锁资源是通过代码实现的。

【 在 philphy (海天之间) 的大作中提到: 】
多谢,这是我看到的唯一一个切题的答复!
p
philphy
25 楼
多谢 其实一万个mutex有点类似于 atomic type

【 在 cxu123 (NY) 的大作中提到: 】
原来你想知道的是这个,这是常识性问题。
当你创建一个新文件的时候,想想操作系统内部怎么锁文件的,每个文件一个锁。1万
个文件同时写,就有一万个锁
c
cxu123
26 楼
真实的内部不见得这么做,一个flag,用atomic操作也可以。但从外面看,就像锁一样。

从逻辑上也得这么做,别人不用这个资源,也被block,逻辑上说不过去

所以必然是只有竞争资源有锁,用的人被block,不相干的人没事。有好多资源,就有
好多锁。

【 在 philphy (海天之间) 的大作中提到: 】
多谢 其实一万个mutex有点类似于 atomic type
w
wdong
27 楼
锁(mutex)都是锁资源. 楼主是同时锁了所有文件, 但还是锁资源.
和代码相关的是unique_lock. 但unique_lock其实不是锁, 而是
用RAII的方式对mutex加锁解锁. unique_lock在一个{} scope内
起作用.

【 在 AlphaCode (Alpha) 的大作中提到: 】
你自己概念混乱而已。代码和资源怎么混为一谈?再说一遍,你锁的是资源,锁代码是
个什么东西?锁资源是通过代码实现的。
t
ts78
28 楼
多线程的同步就是防止多线程操作下引起结果不确定
只要能避免结果不确定, 任何办法都是可以
不强调锁什么, 那是具体实现问题

【 在 philphy (海天之间) 的大作中提到: 】
有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
txt.
现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
mutex mx;
void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
...................
A
AlphaCode
29 楼
是这个道理

【 在 wdong (cybra) 的大作中提到: 】
锁(mutex)都是锁资源. 楼主是同时锁了所有文件, 但还是锁资源.
和代码相关的是unique_lock. 但unique_lock其实不是锁, 而是
用RAII的方式对mutex加锁解锁. unique_lock在一个{} scope内
起作用.
w
wwzz
30 楼
这说到点子上,既不是锁代码,也不是锁资源。

【 在 ts78 (ts) 的大作中提到: 】
多线程的同步就是防止多线程操作下引起结果不确定
只要能避免结果不确定, 任何办法都是可以
不强调锁什么, 那是具体实现问题
a
a9
31 楼
这个写文件靠检测失败不现实,你总不能一直轮询吧

【 在 cxu123 (NY) 的大作中提到: 】
标 题: Re: 关于多线程锁:锁代码还是锁资源?
发信站: BBS 未名空间站 (Sat Sep 24 16:09:13 2016, 美东)

是啊,楼主不能埋头写程序,不管背景知识。

操作系统提供的资源。这些如果是竞争的,基本都有锁,你不用自己搞一个。

你只要使用这些资源和处理竞争资源失败的情况就行了。

: 【 在 digua (姚之FAN) 的大作中提到: 】
: 是的。不同的进程,打开不同的文件,本可以不上锁的(看楼主说的应该是这样)。现
: 在楼主的写法,一个线程(共享内存的多线程)用write_to_file()打开文件1, 另一个
: 线程就不能用write_to_file()打开文件2了。



--
c
cxu123
32 楼
轮询其实也没什么,操作系统也有select(), epoll()之类的系统调用,更加有效率

【 在 a9 (嗯) 的大作中提到: 】
这个写文件靠检测失败不现实,你总不能一直轮询吧
。现
一个
a
a9
33 楼
先不说你这是自找麻烦,你解释一下更有效率从何而来。

【 在 cxu123 (NY) 的大作中提到: 】
标 题: Re: 关于多线程锁:锁代码还是锁资源?
发信站: BBS 未名空间站 (Mon Sep 26 12:30:48 2016, 美东)

轮询其实也没什么,操作系统也有select(), epoll()之类的系统调用,更加有效率

: 【 在 a9 (嗯) 的大作中提到: 】
: 这个写文件靠检测失败不现实,你总不能一直轮询吧
: 。现
: 一个



--
c
cxu123
34 楼
你得先说为啥多线程要同时写一个文件,是多线程下载,还是什么其它应用。

没有实时性要求,当然可以轮询。

有实时性要求,要同时写一个文件,操作文件内部指针,用select,epoll的系统调用
,显然比用mutex去锁这个文件好

【 在 a9 (嗯) 的大作中提到: 】
先不说你这是自找麻烦,你解释一下更有效率从何而来。
a
a9
35 楼
用mutex锁文件又是咋锁的?

【 在 cxu123 (NY) 的大作中提到: 】
标 题: Re: 关于多线程锁:锁代码还是锁资源?
发信站: BBS 未名空间站 (Mon Sep 26 13:00:35 2016, 美东)

你得先说为啥多线程要同时写一个文件,是多线程下载,还是什么其它应用。

没有实时性要求,当然可以轮询。

有实时性要求,要同时写一个文件,操作文件内部指针,用select,epoll的系统调用
,显然比用mutex去锁这个文件好



: 【 在 a9 (嗯) 的大作中提到: 】
: 先不说你这是自找麻烦,你解释一下更有效率从何而来。



--
c
cxu123
36 楼
你没看一楼的问题,又不是真的锁文件,是对文件访问原子操作。

【 在 a9 (嗯) 的大作中提到: 】
用mutex锁文件又是咋锁的?
l
lambdai
37 楼
general地讲,提高performance要尽量把一定不会冲突的partition用不同的锁
你这个case,如果不是hardlink或是symlink的话,肯定是完美的partition,锁资源的方式既符合逻辑实现也不麻烦
鉴于你这个资源是文件,如果能保证相同文件在多线程中fd唯一的话,用flock应该比
用mutex容易实现一些

【 在 philphy (海天之间) 的大作中提到: 】
有一个多线程的问题,我困惑了好久。多线程的mutex锁,是锁资源还是锁代码?
举个例子 我有100个文件存在磁盘上,分别命名为1.txt, 2.txt, 3.txt ...... 100.
txt.
现在我要创建多个线程,去往文件存东西。举例子如下(C++)。
mutex mx;
void write_to_file(string filename, string binary_data)
{
unique_lock lk(mx);
fstream f(filename);
f.write(&(binary_data[0]), binary_data.size());
...................
p
philphy
38 楼
多谢
我觉得所有文件共享一个锁是不合理的,因为这些文件互相独立
他们应该有各自的锁
但是这样的话,创建锁本身也是占用大量的资源

【 在 lambdai (lambdai) 的大作中提到: 】
general地讲,提高performance要尽量把一定不会冲突的partition用不同的锁
你这个case,如果不是hardlink或是symlink的话,肯定是完美的partition,锁资源的
方式既符合逻辑实现也不麻烦
鉴于你这个资源是文件,如果能保证相同文件在多线程中fd唯一的话,用flock应该比
用mutex容易实现一些
l
lambdai
39 楼
锁占的资源很少的,linux下的pthread_mutex实现应该就是几十个字节吧。当然和你的应用有关,但是即使嵌入式应用,这个数量级的资源很可能也是可以忽略不计了。
自己斟酌~
T
TheMatrix
40 楼
这是对的。

【 在 AlphaCode (Alpha) 的大作中提到: 】
不存在锁代码,锁的都是资源
l
lambdai
41 楼
perfbook
- Glossary and Bibliography
-- Code Locking
-- Data Locking
【 在 AlphaCode (Alpha) 的大作中提到: 】
你自己概念混乱而已。代码和资源怎么混为一谈?再说一遍,你锁的是资源,锁代码是
个什么东西?锁资源是通过代码实现的。
h
heteroclinic
42 楼
data or code just a pointer
you have a double-boolean controll read/write associated to the pointer
【 在 lambdai (lambdai) 的大作中提到: 】
: perfbook
: - Glossary and Bibliography
: -- Code Locking
: -- Data Locking