diff --git a/a.out b/a.out new file mode 100755 index 0000000..0cf0c21 Binary files /dev/null and b/a.out differ diff --git a/a.out.dSYM/Contents/Info.plist b/a.out.dSYM/Contents/Info.plist new file mode 100644 index 0000000..3679a65 --- /dev/null +++ b/a.out.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.a.out + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/a.out.dSYM/Contents/Resources/DWARF/a.out b/a.out.dSYM/Contents/Resources/DWARF/a.out new file mode 100644 index 0000000..fb793ef Binary files /dev/null and b/a.out.dSYM/Contents/Resources/DWARF/a.out differ diff --git a/thread.c b/thread.c new file mode 100644 index 0000000..6dd0a64 --- /dev/null +++ b/thread.c @@ -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; +} + + diff --git a/多线程.md b/多线程.md index 7f6dbeb..db1f739 100644 --- a/多线程.md +++ b/多线程.md @@ -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….依次递推。 diff --git a/排序算法/README.md b/排序算法/README.md index ee32828..2139266 100644 --- a/排序算法/README.md +++ b/排序算法/README.md @@ -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[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 diff --git a/算法分析思路/README.md b/算法分析思路/README.md index d3d2def..c626085 100644 --- a/算法分析思路/README.md +++ b/算法分析思路/README.md @@ -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 -案例: -快速排序 -归并排序 -最大子数组和 +*案例* +快速排序 +归并排序 +最大子数组和