This commit is contained in:
nonstriater
2014-05-09 09:51:52 +08:00
parent 04a7c20142
commit 39447dddd1
10 changed files with 370 additions and 77 deletions
Executable
BIN
View File
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.a.out</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
Binary file not shown.
+71
View File
@@ -0,0 +1,71 @@
#include "stdio.h"
#include "pthread.h"
pthread_mutex_t mutex;
pthread_cond_t condition;
unsigned int i = 0;
void *threadFunc1(void *arg){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&condition,&mutex);
pthread_mutex_unlock(&mutex);
printf("aaaaaaa %d\n",i);
pthread_exit((void *)0);
}
void *threadFunc2(void *arg){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&condition,&mutex); //??? 这里人存在问题
pthread_mutex_unlock(&mutex);
printf("vbbbbbbbbbb %d\n",i);
pthread_exit((void *)0);
}
int main(int argc, char const *argv[])
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&condition,NULL);
pthread_t thread1,thread2;
pthread_create(&thread1,NULL,threadFunc1,(void *)0);
pthread_create(&thread2,NULL,threadFunc2,(void *)0);
while(1){
pthread_mutex_lock(&mutex);
i++;
pthread_mutex_unlock(&mutex);
if (i==100000000)
{
//
pthread_cond_broadcast(&condition);
break;
}
}
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
pthread_cond_destroy(&condition);
pthread_mutex_destroy(&mutex);
return 0;
}
+138 -11
View File
@@ -1,27 +1,160 @@
多线程有几种实现方法,都是什么?
### 多线程有几种实现方法,都是什么?
OC中:
Thread: 使用简单方便,但效率不及下面的几种方式
NSOperationQueue:可以对thread的状态做精确的控制,gcd做不到
GCD: 效率做好的方案,且结合block,使用方便
pthread OS提供的api
### 多线程同步和互斥有几种实现方法,都是什么?
首先理解线程`同步`和`互斥`的概念。
`同步` 在一个功能调用时,如果没有得到结果,调用就不返回
`死锁` 2个线程访问共享资源,顺序不当造成的。如一个线程锁定了资源A,想访问另一个线程锁定的资源B,而这个线程又在等待A资源。
`线程同步` 线程A和B一块配合,A执行到一定程度需要拷B的结果,于是停下来,等待B的指向结果。
1. 临界区: 任意时刻只允许一个线程访问共享资源
2. 互斥量: 拥有互斥对象的线程才能访问公共资源
3. 信号量: 允许多个线程同时访问统一资源??
4. 事件 : ?
结合代码来看看 linux上 3种 线程同步的方式:
#### 互斥锁
pthread_mutex_t
pthread_mutex_init()
pthread_mutex_lock() // 在访问共享资源时,加锁,这样别的线程无法访问
pthread_mutex_unlock() // 解锁,这样别的线程可以访问共享资源了
pthread_mutex_destroy()// 释放互斥锁
互斥锁是如何通知到等待锁的线程的?
我猜内核会维护一个 互斥量对多个等待线程(1对多) 的表,在互斥量解锁后,采用某种方式选择一个等待线程唤醒,让其获得互斥锁,如此往复,直到所有等待线程唤醒执行。(具体待参加linux/kernel/mutex.c)
#### 条件变量
条件变量用来等待,阻塞一个线程,等待某个条件发生时触发。条件变量与互斥锁同时使用.
条件变量和互斥量相关联 是一种用于 多线程之间共享数据状态改变的通信机制。
pthread_cond_init()
pthread_cond_destroy()
pthread_cond_wait(cond,mutex) // 条件测试为假,线程就会阻塞;
pthread_cond_signal() // 给出“条件成立”信号
pthread_cond_broadcast()
以生产者消费者问题为例,消费线程在得知队列中没有产品时,将阻塞自己;
生产者线程生产产品后,没有办法激活消费线程,这个时候生产者还要继续运行生产产品,不能用互斥量来实现;如果消费线程采用轮询的方式,无疑又是对CPU的浪费;最好的方式还是生产线程来唤醒消费线程;
那生产线程如何唤醒另一个线程呢?
多线程同步和互斥有几种实现方法,都是什么?
条件变量本身是需要互斥量来保护的,也就是条件的检查实在互斥锁的保护下进行的。 因此pthread_cond_wait(cond,mutex)函数中,传递了一个mutex互斥量用来锁住cond条件变量
iOS中又NSCondition
- wait
- signal
- broadcast
NSCondition 还实现了 NSLocking协议
- lock
- unlock
#### 信号量
又叫 `信号灯`,本质上是一个计数器。
sem_t
sem_init() // 初始化一个信号量,需要指定允许的最大资源计数。如果这里最大计数为1,跟互斥量类似了。
sem_wait() // 给信号量-1 , 如果信号量任然>=0,线程可以继续指向,否则等待
sem_post() // 给信号量+1
sem_destroy() // 销毁信号量
互斥量与临界区比较
可以理解为,`互斥量` 是 `信号量` 的一种特殊情况(max_sem_count==1)
看下信号量的结构(/usr/src/linux/include/linux/sem.h)
struct semaphore {
spinlock_t lock; // 用来确保对count成员的原子操作
unsigned int count;
struct list_head wait_list;// 在信号量+1,给wait的线程或进程发送信号量
};
生产者消费者问题
`线程互斥` : 某一资源同时只允许一个访问者访问。上面介绍的 `互斥量` `临界区` 都可以实现线程的互斥
### 怎么避免死锁?(经验)
A线程
@synchronized(lockobj){
while([datas count==0){...}
}
B线程
@synchronized(lockobj){
[datas addObject:[Item new]];
}
A线程一直在while循环上等待,但是B线程有无法获得lockobj锁而等待出现死锁。解决办法是用NSLock来替换@synchronized(lock),在while()中
1). 在使用锁的程序中,不要在程序中间使用return(这个时候还没有解锁,就可能会造成死锁), 而使用goto函数跳转至解锁并退出函数的位置
多线程同步问题
1. 线程重排序reordering thread
2. minimize locking
3. a bigger loc
4. try lock
5. time out for locking
### 互斥量与临界区比较?
互斥量比临界区复杂,临界区只能完成统一进程内线程直接的同步,互斥量可以实现不同进程的线程的同步,可以跨进程使用。
互斥量,信号量,事件都可以跨进程访问
### 生产者消费者问题
生产者和消费者之间通常有一个缓冲区作为中介。这个缓冲区有以下作用:
1. 解耦合
2. 让 生产者 和 消费者 并发执行
3. 支持忙闲不均
队列缓冲区
环形缓冲区
### 读者写者问题
这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。
### 几个多线程面试题目
主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量。
要求:
@@ -30,12 +163,6 @@
读者写者问题
这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。
编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。
+29 -26
View File
@@ -1,4 +1,6 @@
## 排序算法
**排序的稳定性** 对于相等的元素,排序之后,任然保存2个元素的位置没有变,就是稳定的排序,反之就是不稳定排序。
@@ -19,10 +21,10 @@
递归一次,pivot 左边都比它小,右边都比它大。这是递归,分治的思想。
对 A[p...r] :
1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]<A[q]<A[q+1..r]
2. 解决:递归调用 快排,,对子数组A[p..q-1],A[q+1..r]排序
3. 合并(子问题相互独立的,因此用分治算法就可以了)
对 A[p...r] :
1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]<A[q]<A[q+1..r]
2. 解决:递归调用 快排,,对子数组A[p..q-1],A[q+1..r]排序
3. 合并(子问题相互独立的,因此用分治算法就可以了)
具体步骤:
@@ -31,15 +33,15 @@
3. 递归的,使用相同的方式,重排左右两边的子序列
扫描过程分2种:
1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。
2. 单向扫描
1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。
2. 单向扫描
平均复杂度 O(n*logn)
最坏O(n^2)
空间复杂度
平均复杂度 O(n*logn) ,
最坏O(n^2)
空间复杂度
快速排序是对冒泡排序的改进,划分交换排序。
快速排序是对冒泡排序的改进,划分交换排序。
@@ -76,10 +78,10 @@
2个有序数组的合并操作是O(n)的复杂度
因此我们可以将无序的数组,分成2个子数组分别排序,然后再merge,依次类推
归并排序的步骤
1. 分解 , 将一个数组分成2个子数组
2. 解决 , 将2个子数组都排好序,然后 merge 2个有序数组
3. 合并
归并排序的步骤
1. 分解 , 将一个数组分成2个子数组
2. 解决 , 将2个子数组都排好序,然后 merge 2个有序数组
3. 合并
@@ -101,26 +103,27 @@
堆的操作有:
建堆
插入:都是插入到数组最后,然后再调整满足堆次序
删除:删除总是发生在 A[0]处,也就是只删除根节点
建堆
插入:都是插入到数组最后,然后再调整满足堆次序
删除:删除总是发生在 A[0]处,也就是只删除根节点
这样难怪`堆`被称为 `优先队列`。插入和删除分别在 数组尾部和 头部,只是需要再次调整以满足堆次序。
`堆`的应用场景有:
优先队列 如iOS中的NSOperationQueue 就是维护一个优先队列
堆排序
`堆`的应用场景有:
优先队列 如iOS中的NSOperationQueue 就是维护一个优先队列
堆排序
我们来看看如何使用`堆` 来做排序。
我们来看看如何使用`堆` 来做排序?
1.将待排序数列看做一颗完全二叉树的存储结构
2.`堆化数组`,结束后,根a[0]变成了最小的值(小根堆)
3.取a[0]值,然后对`堆`做删除操作,此时,`堆`会重新 `堆化数组`,a[0]又是下一个最小的值
4. 循环3,就可以按从小到大的顺序取出所有数组元素。
1.将待排序数列看做一颗完全二叉树的存储结构
2.`堆化数组`,结束后,根a[0]变成了最小的值(小根堆)
3.取a[0]值,然后对`堆`做删除操作,此时,`堆`会重新 `堆化数组`,a[0]又是下一个最小的值。
删除操作通常是先把数组最后的元素提到a[0]位置,然后从根节点开始进行一次从上向下的调整;调整时,先从左右孩子中找最小的交换。如果父节点比每个节点都小就不用调整。(因此,在`堆排序`是可以直接让 a[0]和数组最后一个元素互换,但要先保存好a[0],或者a[n-1],这样导致了使用`堆排序`时,递增排序使用大根堆,递减排序使用小根堆。)
4. 循环3,就可以按从小到大的顺序取出所有数组元素。
堆排序主要时间花在建堆期间和`堆化数组`,找数列中最大树只需要O(1)时间复杂度
堆排序主要时间花在建堆期间和`堆化数组`,找数列中最大树只需要O(1)时间复杂度
Binary file not shown.
+84 -12
View File
@@ -131,7 +131,7 @@ void quicksort(int *a, int left, int right){
void merge_sort(int *a, int length){
}
@@ -139,33 +139,105 @@ void merge_sort(int *a, int length){
// 堆排序
void heap_sort(int *a, int length){
void heap_swop(int *a,int *b){
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
//自顶向下调整
void heap_public_adjust(int *a,int i,int length){
#define Max_Number 50000
// 三个数里取最大的一个 a[i],a[2i+1],a[2i+2],跟a[i]交换;然后是 a[(i-1)/2],a[i],a[i+1] .. 一直到a[0]
int tmp = i , max;//
while(2*tmp+1<length){
max = 2*tmp+1;
//三个数里取最大的一个 a[tmp],a[2tmp+1],a[2tmp+2]
if (2*tmp+2<length)// 越界检查
{
if (a[max] < a[2*tmp+2])// 先选出最大的子节点
{
max = 2*tmp+2;
}
}
// 和最大孩子比
if (a[tmp] >= a[max])
{
break;
}
else
{
heap_swop(&a[tmp],&a[max]);
tmp = 2*tmp+1;
}
}
}
// 从第一个非叶子节点a[(length-2)/2],开始做调整, 跟自己的子节点比较,把最大的孩子换上来就是创建最大堆,
//反之,把最小的孩子换上来就是创建最小堆 一直到a[0]
void heap_build(int *a,int length){
for (int i = (length-2)/2; i >=0 ; --i)
{
// 三个数里取最大的一个 a[i],a[2i+1],a[2i+2],跟a[i]交换;然后是 a[(i-1)/2],a[i],a[i+1] .. 一直到a[0]
heap_public_adjust(a,i,length);
}
}
// 自顶向下调整
void heap_adjust(int *a,int length){
heap_public_adjust(a,0,length); //对0号调整
}
void heap_sort(int *a, int length){
// 建立堆 大根堆,递增排序
heap_build(a,length);
for (int i = length-1; i >0; --i)
{
//交换
heap_swop(&a[0],&a[i]);
//调整
heap_adjust(a,i);
}
}
///////////////////////////////////////////////
#define Max_Number 50
int main(){
//int a[] = {4,87,2,32,5,41,2,9};
int a[] = {4,87,2,32,5,2,9,49,49,23,45,2,41};
// 准备5000个数
int a[Max_Number];
for (int i = 0; i < Max_Number; ++i)
{
a[i]=rand()%Max_Number;
}
// int a[Max_Number];
// for (int i = 0; i < Max_Number; ++i)
// {
// a[i]=rand()%Max_Number;
// }
clock_t start,finish;
start = clock();
heap_sort(a,sizeof(a)/sizeof(int)); // 有buggggggg
//quicksort(a,0,sizeof(a)/sizeof(int)-1); // 0.01s
//insert_sort(a,sizeof(a)/sizeof(int)); // 3.85s
//select_sort(a,sizeof(a)/sizeof(int)); // 5.3s
+28 -28
View File
@@ -4,10 +4,10 @@
是一种不断用旧值递推新值的过程,分精确迭代和近视迭代。是用来求方程和方程组近似根的方法。
迭代变量
迭代关系, 迭代关系选择不合理,会导致迭代失败
迭代过程控制,也就是迭代什么时候结束,不能无休止进行下去
迭代变量
迭代关系, 迭代关系选择不合理,会导致迭代失败
迭代过程控制,也就是迭代什么时候结束,不能无休止进行下去
@@ -38,17 +38,17 @@
在 `递推` 阶段,将大的问题分解成小的问题
在 `回归` 阶段,获得最简单问题的解后,逐级返回,依次得到稍微复杂情况的解,知道获得最终的结果
1) 确定递归公司
2) 确定边界条件
1) 确定递归公司
2) 确定边界条件
如
斐波那契数列 fib(n)=fib(n-1)+fib(n-2)
阶乘计算
梵塔问题 (三根针1,2,3表示,1号从小到大n个盘子,先要都移到3号上,不能出现大盘压小盘,找出移动次数最少的方案)
快速排序
斐波那契数列 fib(n)=fib(n-1)+fib(n-2)
阶乘计算
梵塔问题 (三根针1,2,3表示,1号从小到大n个盘子,先要都移到3号上,不能出现大盘压小盘,找出移动次数最少的方案)
快速排序
递归运行效率较低,因为有函数调用的开销,递归多次也可能造成栈溢出。
@@ -80,7 +80,7 @@
案例:
迷宫问题
迷宫问题
@@ -92,10 +92,10 @@
案例
找回零钱问题
装箱问题
赫夫曼编码
*案例*
找回零钱问题
装箱问题
赫夫曼编码
@@ -106,15 +106,15 @@
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,各个击破,分而治之。
分治算法常用递归实现
1) 问题缩小的小规模可以很容易解决
2) 问题可以分解为规模较小相同问题
3) 子问题的解可以合并为该问题的解
4) 各个子问题相互独立,(如果这条不满足,转为`动态规划`求解)
1) 问题缩小的小规模可以很容易解决
2) 问题可以分解为规模较小相同问题
3) 子问题的解可以合并为该问题的解
4) 各个子问题相互独立,(如果这条不满足,转为`动态规划`求解)
分治法的步骤:
1. 分解
2. 解决
3. 合并
1. 分解
2. 解决
3. 合并
以 `大整数乘法` 为例
@@ -122,10 +122,10 @@
如 26542123532213598*345987342245553677884
案例:
快速排序
归并排序
最大子数组和
*案例*
快速排序
归并排序
最大子数组和