Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7553dac

Browse files
committed
add leetcode 78
add leetcode 78
1 parent ee02f19 commit 7553dac

File tree

4 files changed

+392
-6
lines changed

4 files changed

+392
-6
lines changed

Android/Sync.md

Lines changed: 241 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,251 @@
1-
### 安卓源码阅读编译相关分析
1+
### 安卓当中的同步机制
22

3-
现在 网上有很多编译的教程,我这里就不在赘述。这里主要记录我阅读安卓源码和修改的一些心得体会。
3+
我们先回忆一下,操作系统中有哪些同步的手段?
44

5-
感谢老罗,感谢前人带我入门!
5+
- 信号量(Semaphore)
66

7+
它主要包含两个操作,P和V。 Semaphore 来指示这个共享资源的可用数量,换种理解是,有多少人可以使用这个资源?
78

9+
P操作可以减少信号量的计数,V操作可以增加计数。P V 这两个操作都是属于原子操作,意味着执行过程不可以被中断。
810

9-
注意,我这里的源代码版本是android - 10.不同代码版本结构稍有不同,这里请自行熟悉。
11+
- 互斥量(Mutex)
1012

13+
简单而言,你可以认为他是简化版的信号量。也就是取值只能为0和1的信号量(Binary Semaphore)。换句话说,操作系统很多时候的资源都具有排他性--这个资源当前要么被占用,要么可以被访问。Mutex相较于Semaphore实现起来也更为简单。
1114

15+
- 管程(Monitor)
1216

13-
[安卓中
17+
- Futex(Fast Userspace mutexs)Linux独有的,好像在早期的某个内核版本被加入。
1418

15-
[同步机制](Android/Sync.md)
1619

20+
21+
那么我们来看看Android中有哪些手段吧。
22+
23+
#### Mutex
24+
25+
文件位置:`$ANDROID_CODEBASE/system/core/libutils/include/utils/Mutex.h`,如果下面没有特殊说明,默认我们的路径都是从`$ANDROID_CODEBASE`开始。它表示你的源码根目录。
26+
27+
为了简单,我就摘取一些相关的代码,其他代码有兴趣的可以自行深入分析。
28+
29+
```cpp
30+
class CAPABILITY("mutex") Mutex {
31+
public:
32+
enum {
33+
PRIVATE = 0, // 只支持同一个进程间的同步
34+
SHARED = 1 // 支持跨进程间的同步
35+
};
36+
37+
Mutex();
38+
explicit Mutex(const char* name);
39+
explicit Mutex(int type, const char* name = nullptr);
40+
~Mutex();
41+
42+
// lock or unlock the mutex
43+
status_t lock() ACQUIRE();
44+
void unlock() RELEASE();
45+
46+
// lock if possible; returns 0 on success, error otherwise
47+
status_t tryLock() TRY_ACQUIRE(0);
48+
...
49+
```
50+
51+
52+
53+
两个枚举量的意思我已经注释中写明白了,这里只有三个有意思的方法,
54+
55+
```c++
56+
status_t lock();
57+
void unlock();
58+
status_t tryLock();
59+
```
60+
61+
从名字上我们可以看到他们的作用,我们来进一步看看他的实现:
62+
63+
```c++
64+
inline status_t Mutex::lock() {
65+
return -pthread_mutex_lock(&mMutex);
66+
}
67+
inline void Mutex::unlock() {
68+
pthread_mutex_unlock(&mMutex);
69+
}
70+
inline status_t Mutex::tryLock() {
71+
return -pthread_mutex_trylock(&mMutex);
72+
}
73+
```
74+
75+
这里你就会发现,他本质上其实是对pthread的接口做了一些相关的封装 。
76+
77+
#### Condition
78+
79+
这个东西字面意思是条件。他的设计哲学是,判断一个条件是不是满足了?—— 如果满足了,那就返回,继续执行下去,否则就休眠等待。**直到条件被满足**
80+
81+
退一步想想,这种情况能用Mutex做么? 理论上应该可以的。举个例子,假设我们两个线程`A`` B`, 他们会同时修改一个全局变量`var`, 并且我们定义行为:
82+
83+
- `Thread A` 不断修改 `var`的值,每次改变之后的值是未知的
84+
85+
- `Thread B`观察`var`的值,当`var = 0`的时候执行某些动作。
86+
87+
我们可以看到, A , B 两个线程都想访问`var`这个资源。Mutex是个思路。但是我们想想,线程B 等待的是当 `var`这个变量为0的情况,醉翁之意不在酒。
88+
89+
如果用Mutex写,类似于这样的:
90+
91+
```c
92+
while (1) { // 死循环
93+
acquire_mutex_lock(var); // 去获取mutex锁
94+
if (var == 0) { // 条件满足
95+
release_mutex_lock(var);
96+
break;
97+
} else {
98+
release_mutex_lock(var); // 下一轮我们再看看
99+
sleep();
100+
}
101+
}
102+
```
103+
104+
所以我们可以看到,这种轮询方式特别耗费CPU时间。
105+
106+
举个例子,假如有两个角色,厕所维护员还有使用厕所的用户,代表上述A B 两个角色。然后我们把厕纸当做变量var。那么出现这种场景,用户随便用厕纸,厕纸的余量是多少未知。但是工作人员等厕纸余量为0的时候,需要去更换厕纸。Mutex的机制下,可以看到,工作人员和普通拉屎的用户一样,都要排队轮询进厕所来看看厕纸。那么可以看到,这个工作人员效率很低,他要跟大家一起排队来获取进入厕所的机会。
107+
108+
那么有一种思路是,工作人员不排队,当厕纸用完了,有个人通知他,叫他进来换厕纸,减少排队数量,提高效率。
109+
110+
Condition就是来解决这类问题的。
111+
112+
- 文件位置:`system/core/libutils/include/utils/Condition.h`
113+
114+
```c++
115+
class Condition {
116+
public:
117+
enum {
118+
PRIVATE = 0, // 和前面类似,也有跨进程共享的支持
119+
SHARED = 1
120+
};
121+
122+
enum WakeUpType {
123+
WAKE_UP_ONE = 0,
124+
WAKE_UP_ALL = 1
125+
};
126+
127+
Condition();
128+
explicit Condition(int type);
129+
~Condition();
130+
// Wait on the condition variable. Lock the mutex before calling.
131+
// Note that spurious wake-ups may happen.
132+
status_t wait(Mutex& mutex); // 在某个条件上等待
133+
// same with relative timeout
134+
status_t waitRelative(Mutex& mutex, nsecs_t reltime); // 同上,但是这里是超时退出
135+
// Signal the condition variable, allowing one thread to continue.
136+
void signal(); // 满足条件了,通知等待者
137+
// Signal the condition variable, allowing one or all threads to continue.
138+
void signal(WakeUpType type) {
139+
if (type == WAKE_UP_ONE) {
140+
signal();
141+
} else {
142+
broadcast();
143+
}
144+
}
145+
// Signal the condition variable, allowing all threads to continue.
146+
void broadcast(); // 条件满足时 通知所有等待者
147+
```
148+
149+
这里是个C++的类,那么我们来看看这里的一些关键实现方法。
150+
151+
```c++
152+
inline status_t Condition::wait(Mutex& mutex) {
153+
return -pthread_cond_wait(&mCond, &mutex.mMutex);
154+
}
155+
156+
inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
157+
struct timespec ts;
158+
#if defined(__linux__)
159+
clock_gettime(CLOCK_MONOTONIC, &ts); // linux和apple下获取时间的函数,编译相关
160+
#else // __APPLE__
161+
// Apple doesn't support POSIX clocks.
162+
struct timeval t;
163+
gettimeofday(&t, nullptr);
164+
ts.tv_sec = t.tv_sec;
165+
ts.tv_nsec = t.tv_usec*1000;
166+
#endif
167+
168+
// On 32-bit devices, tv_sec is 32-bit, but `reltime` is 64-bit.
169+
int64_t reltime_sec = reltime/1000000000;
170+
171+
ts.tv_nsec += static_cast<long>(reltime%1000000000);
172+
if (reltime_sec < INT64_MAX && ts.tv_nsec >= 1000000000) {
173+
ts.tv_nsec -= 1000000000;
174+
++reltime_sec;
175+
}
176+
177+
int64_t time_sec = ts.tv_sec;
178+
if (time_sec > INT64_MAX - reltime_sec) {
179+
time_sec = INT64_MAX;
180+
} else {
181+
time_sec += reltime_sec;
182+
}
183+
184+
ts.tv_sec = (time_sec > LONG_MAX) ? LONG_MAX : static_cast<long>(time_sec);
185+
186+
return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
187+
}
188+
189+
190+
inline void Condition::signal() {
191+
pthread_cond_signal(&mCond);
192+
}
193+
inline void Condition::broadcast() {
194+
pthread_cond_broadcast(&mCond);
195+
}
196+
197+
```
198+
199+
可以看到,还是pthread的相关接口的封装。
200+
201+
这里留个问题,为什么wait函数的参数还是要用到Mutex呢?
202+
203+
#### Barrier
204+
205+
意思是屏障。接上文我们继续思考,我们来看一个Condition的例子。
206+
207+
Barrier这个东西是为了SurfaceFlinger这个东西设计的,不像Mutex、Condition一样作为Util工具来提供给大家用。不过我们来看看Barrier这个例子。
208+
209+
文件位置:`frameworks/native/services/surfaceflinger/Barrier.h`
210+
211+
```c++
212+
class Barrier
213+
{
214+
public:
215+
// Release any threads waiting at the Barrier.
216+
// Provides release semantics: preceding loads and stores will be visible
217+
// to other threads before they wake up.
218+
void open() {
219+
std::lock_guard<std::mutex> lock(mMutex);
220+
mIsOpen = true;
221+
mCondition.notify_all();
222+
}
223+
224+
// Reset the Barrier, so wait() will block until open() has been called.
225+
void close() {
226+
std::lock_guard<std::mutex> lock(mMutex);
227+
mIsOpen = false;
228+
}
229+
230+
// Wait until the Barrier is OPEN.
231+
// Provides acquire semantics: no subsequent loads or stores will occur
232+
// until wait() returns.
233+
void wait() const {
234+
std::unique_lock<std::mutex> lock(mMutex);
235+
mCondition.wait(lock, [this]() NO_THREAD_SAFETY_ANALYSIS { return mIsOpen; });
236+
}
237+
private:
238+
mutable std::mutex mMutex;
239+
mutable std::condition_variable mCondition;
240+
int mIsOpen GUARDED_BY(mMutex){false};
241+
};
242+
243+
```
244+
245+
可以看到,有三个函数,分别是`wait()`, `close()`,`open()`
246+
247+
既然说它是condition的一个例子,那么barrier等待的条件是什么呢?
248+
249+
答案是 `mIsOpen`。我们来看`wait`函数,他首先获取了一个mutex锁,然后才去调用Condition的`wait`。为什么呢?因为`mIsOpen`这个变量如果没有被互斥锁保护起来的话,`open/close`同时去操作他的话,会发生什么情况呢?
250+
251+
所以这就是整个设计的一个巧妙的地方。

Backtrace/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### 回溯思想
2+
3+
- [Leetcode78 子集](Backtrace/leetcode78.md)

0 commit comments

Comments
 (0)