C# 多线程及同步简介示例

60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;二是由于对称多处理机(SMP)出现,可以满足多个运行单位,而多个进程并行开销过大。
因此在80年代,出现了能独立运行的基本单位——线程(Threads)。
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

 

一、线程简义

1、进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源。

2、前台线程和后台线程:通过Thread类新建线程默认为前台线程。当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常。

3、挂起(Suspend)和唤醒(Resume):由于线程的执行顺序和程序的执行情况不可预知,所以使用挂起和唤醒容易发生死锁的情况,在实际应用中应该尽量少用。

4、阻塞线程:Join,阻塞调用线程,直到该线程终止。

5、终止线程:Abort:抛出 ThreadAbortException 异常让线程终止,终止后的线程不可唤醒。Interrupt:抛出 ThreadInterruptException 异常让线程终止,通过捕获异常可以继续执行。

6、线程优先级:AboveNormal BelowNormal Highest Lowest Normal,默认为Normal。

二、线程的使用

线程函数通过委托传递,可以不带参数,也可以带参数(只能有一个参数),可以用一个类或结构体封装参数。

 1 namespace Test
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Thread t1 = new Thread(new ThreadStart(TestMethod));
 8             Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
 9             t1.IsBackground = true;
10             t2.IsBackground = true;
11             t1.Start();
12             t2.Start("hello");
13             Console.ReadKey();
14         }
15 
16         public static void TestMethod()
17         {
18             Console.WriteLine("不带参数的线程函数");
19         }
20 
21         public static void TestMethod(object data)
22         {
23             string datastr = data as string;
24             Console.WriteLine("带参数的线程函数,参数为:{0}", datastr);
25         }
26     } 
27 }

 

三、线程池

由于线程的创建和销毁需要耗费一定的开销,过多的使用线程会造成内存资源的浪费,出于对性能的考虑,于是引入了线程池的概念。线程池维护一个请求队列,线程池的代码从队列提取任务,然后委派给线程池的一个线程执行,线程执行完不会被立即销毁,这样既可以在后台执行任务,又可以减少线程创建和销毁所带来的开销。

线程池线程默认为后台线程(IsBackground)。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //将工作项加入到线程池队列中,这里可以传递一个线程参数
 6             ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
 7             Console.ReadKey();
 8         }
 9 
10         public static void TestMethod(object data)
11         {
12             string datastr = data as string;
13             Console.WriteLine(datastr);
14         }
15     }

 

四、Task类

使用ThreadPool的QueueUserWorkItem()方法发起一次异步的线程执行很简单,但是该方法最大的问题是没有一个内建的机制让你知道操作什么时候完成,有没有一个内建的机制在操作完成后获得一个返回值。为此,可以使用System.Threading.Tasks中的Task类。

构造一个Task<TResult>对象,并为泛型TResult参数传递一个操作的返回类型。

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
 6             t.Start();
 7             t.Wait();
 8             Console.WriteLine(t.Result);
 9             Console.ReadKey();
10         }
11 
12         private static Int32 Sum(Int32 n)
13         {
14             Int32 sum = 0;
15             for (; n > 0; --n)
16                 checked{ sum += n;} //结果太大,抛出异常
17             return sum;
18         }
19     }

 

一个任务完成时,自动启动一个新任务。
一个任务完成后,它可以启动另一个任务,下面重写了前面的代码,不阻塞任何线程。

 1  class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
 6             t.Start();
 7             //t.Wait();
 8             Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}",t.Result));
 9             Console.ReadKey();
10         }
11 
12         private static Int32 Sum(Int32 n)
13         {
14             Int32 sum = 0;
15             for (; n > 0; --n)
16                 checked{ sum += n;} //结果溢出,抛出异常
17             return sum;
18         }
19     }

 

五、委托异步执行

委托的异步调用:BeginInvoke() 和 EndInvoke()

 1  public delegate string MyDelegate(object data);
 2     class Program
 3     {
 4         static void Main(string[] args)
 5         {
 6             MyDelegate mydelegate = new MyDelegate(TestMethod);
 7             IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param");
 8 
 9             //异步执行完成
10             string resultstr = mydelegate.EndInvoke(result);
11         }
12 
13         //线程函数
14         public static string TestMethod(object data)
15         {
16             string datastr = data as string;
17             return datastr;
18         }
19 
20         //异步回调函数
21         public static void TestCallback(IAsyncResult data)
22         {
23             Console.WriteLine(data.AsyncState);
24         }
25     }

 

六、线程同步

  1)原子操作(Interlocked):帮助保护免受计划程序切换上下文时某个线程正在更新可以由其他线程访问的变量或者在单独的处理器上同时执行两个线程就可能出现的错误。 此类的成员不会引发异常。

 1 class Program
 2     {
 3         static int counter = 1;
 4 
 5         static void Main(string[] args)
 6         {
 7             Thread t1 = new Thread(new ThreadStart(F1));
 8             Thread t2 = new Thread(new ThreadStart(F2));
 9 
10             t1.Start();
11             t2.Start();
12 
13             t1.Join();
14             t2.Join();
15 
16             System.Console.ReadKey();
17         }
18 
19         static void F1()
20         {
21             for (int i = 0; i < 5; i++)
22             {
23                 Interlocked.Increment(ref counter);
24                 System.Console.WriteLine("Counter++ {0}", counter);
25                 Thread.Sleep(10);
26             }
27         }
28 
29         static void F2()
30         {
31             for (int i = 0; i < 5; i++)
32             {
33                 Interlocked.Decrement(ref counter);
34                 System.Console.WriteLine("Counter-- {0}", counter);
35                 Thread.Sleep(10);
36             }
37         }
38     }

 

  2)lock()语句:避免锁定public类型,否则实例将超出代码控制的范围,定义private对象来锁定。而自定义类推荐用私有的只读静态对象,比如:private static readonly object obj = new object();为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。Array 类型提供 SyncRoot。许多集合类型也提供 SyncRoot。

  3)Monitor实现线程同步

    通过Monitor.Enter() 和 Monitor.Exit()实现排它锁的获取和释放,获取之后独占资源,不允许其他线程访问。

    还有一个TryEnter方法,请求不到资源时不会阻塞等待,可以设置超时时间,获取不到直接返回false。

 1         public void MonitorSomeThing()
 2         {
 3             try
 4             {
 5                 Monitor.Enter(obj);
 6                 dosomething();
 7             }
 8             catch(Exception ex)
 9             {
10                 
11             }
12             finally
13             {
14                 Monitor.Exit(obj);
15             }
16         }

 

  4)ReaderWriterLock

    当对资源操作读多写少的时候,为了提高资源的利用率,让读操作锁为共享锁,多个线程可以并发读取资源,而写操作为独占锁,只允许一个线程操作。

  1 class SynchronizedCache  
  2     {  
  3         private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();  
  4         private Dictionary<int, string> innerCache = new Dictionary<int, string>();  
  5   
  6         public string Read(int key)  
  7         {  
  8             cacheLock.EnterReadLock();  
  9             try  
 10             {  
 11                 return innerCache[key];  
 12             }  
 13             finally  
 14             {  
 15                 cacheLock.ExitReaderLock();  
 16             }  
 17         }  
 18   
 19         public void Add(int key, string value)  
 20         {  
 21             cacheLock.EnterWriteLock();  
 22             try  
 23             {  
 24                 innerCache.Add(key, value);  
 25             }  
 26             finally  
 27             {  
 28                 cacheLock.ExitWriteLock();  
 29             }  
 30         }  
 31   
 32         public bool AddWithTimeout(int key, string value, int timeout)  
 33         {  
 34             if (cacheLock.TryEnterWriteLock(timeout))  
 35             {  
 36                 try  
 37                 {  
 38                     innerCache.Add(key, value);  
 39                 }  
 40                 finally  
 41                 {  
 42                     cacheLock.ExitReaderLock();  
 43                 }  
 44                 return true;  
 45             }  
 46             else  
 47             {  
 48                 return false;  
 49             }  
 50         }  
 51   
 52         public AddOrUpdateStatus AddOrUpdate(int key, string value)  
 53         {  
 54             cacheLock.EnterUpgradeableReadLock();  
 55             try  
 56             {  
 57                 string result = null;  
 58                 if (innerCache.TryGetValue(key, out result))  
 59                 {  
 60                     if (result == value)  
 61                     {  
 62                         return AddOrUpdateStatus.Unchanged;  
 63                     }  
 64                     else  
 65                     {  
 66                         cacheLock.EnterWriteLock();  
 67                         try  
 68                         {  
 69                             innerCache[key] = value;  
 70                         }  
 71                         finally  
 72                         {  
 73                             cacheLock.ExitWriteLock();  
 74                         }  
 75                         return AddOrUpdateStatus.Updated;  
 76                     }  
 77                 }  
 78                 else  
 79                 {  
 80                     cacheLock.EnterWriteLock();  
 81                     try  
 82                     {  
 83                         innerCache.Add(key, value);  
 84                     }  
 85                     finally  
 86                     {  
 87                         cacheLock.ExitWriteLock();  
 88                     }  
 89                     return AddOrUpdateStatus.Added;  
 90                 }  
 91             }  
 92             finally  
 93             {  
 94                 cacheLock.ExitUpgradeableReadLock();  
 95             }  
 96         }  
 97   
 98         public void Delete(int key)  
 99         {  
100             cacheLock.EnterWriteLock();  
101             try  
102             {  
103                 innerCache.Remove(key);  
104             }  
105             finally  
106             {  
107                 cacheLock.ExitWriteLock();  
108             }  
109         }  
110   
111         public enum AddOrUpdateStatus  
112         {  
113             Added,  
114             Updated,  
115             Unchanged  
116         };  
117     }

 

  5)事件(Event)类实现同步

    事件类有两种状态,终止状态和非终止状态,终止状态时调用WaitOne可以请求成功,通过Set将时间状态设置为终止状态。

    1)AutoResetEvent(自动重置事件)

    2)ManualResetEvent(手动重置事件)

              AutoResetEvent和ManualResetEvent这两个类经常用到, 他们的用法很类似,但也有区别。Set方法将信号置为发送状态,Reset方法将信号置为不发送状态,WaitOne等待信号的发送。可以通过构造函数的参数值来决定其初始状态,若为true则非阻塞状态,为false为阻塞状态。如果某个线程调用WaitOne方法,则当信号处于发送状态时,该线程会得到信号, 继续向下执行。其区别就在调用后,AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程;而ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。

  6)信号量(Semaphore)

      信号量是由内核对象维护的int变量,为0时,线程阻塞,大于0时解除阻塞,当一个信号量上的等待线程解除阻塞后,信号量计数+1。

      线程通过WaitOne将信号量减1,通过Release将信号量加1,使用很简单。

 1         public Thread thrd;
 2         //创建一个可授权2个许可证的信号量,且初始值为2
 3         static Semaphore sem = new Semaphore(2, 2);
 4  
 5         public mythread(string name)
 6         {
 7             thrd = new Thread(this.run);
 8             thrd.Name = name;
 9             thrd.Start();
10         }
11         void run()
12         {
13             Console.WriteLine(thrd.Name + "正在等待一个许可证……");
14             //申请一个许可证
15             sem.WaitOne();
16             Console.WriteLine(thrd.Name + "申请到许可证……");
17             for (int i = 0; i < 4 ; i++)
18             {
19                 Console.WriteLine(thrd.Name + "" + i);
20                 Thread.Sleep(1000);
21             }
22             Console.WriteLine(thrd.Name + " 释放许可证……");
23             //释放
24             sem.Release();
25         }
26     }
27  
28     class mysemaphore
29     {
30         public static void Main()
31         {
32             mythread mythrd1 = new mythread("Thrd #1");
33             mythread mythrd2 = new mythread("Thrd #2");
34             mythread mythrd3 = new mythread("Thrd #3");
35             mythread mythrd4 = new mythread("Thrd #4");
36             mythrd1.thrd.Join();
37             mythrd2.thrd.Join();
38             mythrd3.thrd.Join();
39             mythrd4.thrd.Join();
40         }
41     } 

 

  7)互斥体(Mutex)

      独占资源,可以把Mutex看作一个出租车,乘客看作线程。乘客首先等车,然后上车,最后下车。当一个乘客在车上时,其他乘客就只有等他下车以后才可以上车。而线程与C# Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待C# Mutex对象被释放,如果它等待的C# Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个C# Mutex对象的线程都只有等待。

 1 class Test
 2     {
 3         /// <summary>
 4         /// 应用程序的主入口点。
 5         /// </summary>
 6         [STAThread]
 7         static void Main(string[] args)
 8         {
 9             bool flag = false;
10             System.Threading.Mutex mutex = new System.Threading.Mutex(true, "Test", out flag);
11             //第一个参数:true--给调用线程赋予互斥体的初始所属权
12             //第一个参数:互斥体的名称
13             //第三个参数:返回值,如果调用线程已被授予互斥体的初始所属权,则返回true
14             if (flag)
15             {
16                 Console.Write("Running");
17             }
18             else
19             {
20                 Console.Write("Another is Running");
21                 System.Threading.Thread.Sleep(5000);//线程挂起5秒钟
22                 Environment.Exit(1);//退出程序
23             }
24             Console.ReadLine();
25         }
26     }

 

   8)跨进程间的同步

      通过设置同步对象的名称就可以实现系统级的同步,不同应用程序通过同步对象的名称识别不同同步对象。

 1  static void Main(string[] args)
 2         {
 3             string MutexName = "InterProcessSyncName";
 4             Mutex SyncNamed;     //声明一个已命名的互斥对象
 5              try
 6             {
 7                 SyncNamed = Mutex.OpenExisting(MutexName);       //如果此命名互斥对象已存在则请求打开
 8             }
 9             catch (WaitHandleCannotBeOpenedException)
10             {
11                 SyncNamed = new Mutex(false, MutexName);         //如果初次运行没有已命名的互斥对象则创建一个
12             }
13             Task MulTesk = new Task
14                 (
15                     () =>                  //多任务并行计算中的匿名方法,用委托也可以
16                     {
17                         for (; ; )         //为了效果明显而设计
18                         {
19                             Console.WriteLine("当前进程等待获取互斥访问权......");
20                             SyncNamed.WaitOne();
21                             Console.WriteLine("获取互斥访问权,访问资源完毕,按回车释放互斥资料访问权.");
22                             Console.ReadLine();
23                             SyncNamed.ReleaseMutex();
24                             Console.WriteLine("已释放互斥访问权。");
25                         }
26                     }
27                 );
28             MulTesk.Start();
29             MulTesk.Wait();
30         }

   9)分布式的同步

  可以使用redis任务队列或者redis相关特性

 1                     Parallel.For(0, 1000000, i =>
 2                     {
 3                         Stopwatch sw1 = new Stopwatch();
 4                         sw1.Start();
 5 
 6                         if (redisHelper.GetRedisOperation().Lock(key))
 7                         {
 8                             var tt = int.Parse(redisHelper.GetRedisOperation().StringGet("calc"));
 9 
10                             tt++;
11 
12                             redisHelper.GetRedisOperation().StringSet("calc", tt.ToString());
13 
14                             redisHelper.GetRedisOperation().UnLock(key);
15                         }
16                         var v = sw1.ElapsedMilliseconds;
17                         if (v >= 10 * 1000)
18                         {
19                             Console.Write("f");
20                         }
21                         sw1.Stop();
22                     });

 

 

 


转载请标明本文来源:http://www.cnblogs.com/yswenli/p/7421475.html 
更多内容欢迎star作者的github:https://github.com/yswenli/
如果发现本文有什么问题和任何建议,也随时欢迎交流~

 

转载于:https://www.cnblogs.com/yswenli/p/7421475.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252015.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Framework 5.1.6、5.0.13 与 4.3.23 发布

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; Spring Framework 5.1.6、5.0.13 与 4.3.23 发布了。 v5.1.6 包含 49 个 bug 修复和功能改进&#xff1b;v5.0.13 是 5.0.x 系列的最后一个版本&#xff0c;包含了 25 个 bug 修复和功能改进&am…

单目视觉标定:世界坐标系、相机坐标系、图像坐标系、像素坐标系——简单粗暴,粗暴

转&#xff1a;https://blog.csdn.net/chentravelling/article/details/53558096 1.正文 图像处理、立体视觉等等方向常常涉及到四个坐标系&#xff1a;世界坐标系、相机坐标系、图像坐标系、像素坐标系。例如下图&#xff1a; 构建世界坐标系只是为了更好的描述相机的位置在哪…

VSFTP的使用

一、基本安装 1.安装服务 yum -y install vsftpd //centos Redhat apt-get install vsftpd //debian ubuntu 2.开启服务 service vsftpd status //查看状态&#xff0c;默认是关闭的 service vsftpd start 3.开机随操作系统启动 chkconfig vsftpd on4.添加账号 useradd -d /…

OC Swift混编-Swift.h File not found

https://www.jianshu.com/p/f860fe1718ca 2016.09.13 11:53* 字数 266 阅读 1935评论 1喜欢 1今天碰到个神坑,本人项目是OC项目,最近开始使用Swift开始替代.但是,在替代的已开始就出现问题了:新建swift文件,然后提示创建bridging-Hearder文件,然后swift可以使用OC代码了.这些都…

视觉标定,再来一波!!更简单粗暴!!!!!!

继续&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 一、四个坐标系简介和转换 相机模型为以后一切标定算法的关键&#xff0c;只有这边有相当透彻的理解&#xff0c;对以后的标定算法才能有更好的理解。本人研究了好长时间&#xff0c;几乎每天…

深入浅出NIO之Selector实现原理

2019独角兽企业重金招聘Python工程师标准>>> 前言 Java NIO 由以下几个核心部分组成&#xff1a; 1、Buffer 2、Channel 3、Selector Buffer和Channel在深入浅出NIO之Channel、Buffer一文中已经介绍过&#xff0c;本文主要讲解NIO的Selector实现原理。 之前进行sock…

介绍一下画图小能手matplotlib。

我们在做完数据分析的时候需要把分析出来的结果&#xff0c;做一个图形化的形象表达&#xff0c;这里我们就需要用到画图小能手matplotlib&#xff0c;下面就演示一下常用的条形图和折线图 散点图 散点图的做大的作用是研究两个变量的相关性&#xff08;正相关&#xff0c;负相…

立体视觉标定源代码C++,简单粗暴!粗暴·······

疑点解答&#xff1a; 摄像机矩阵由内参矩阵和外参矩阵组成&#xff0c;对摄像机矩阵进行QR分解可以得到内参矩阵和外参矩阵。 内参包括焦距、主点、倾斜系数、畸变系数 &#xff08;1&#xff09; 其中&#xff0c;fx&#xff0c;fy为焦距&#xff0c;一般情况下&#xff…

MongoDB负载信息一目了然 阿里云HDM重磅发布MongoDB监控和诊断功

2019独角兽企业重金招聘Python工程师标准>>> 混合云数据库管理&#xff08;HDM&#xff09;的统一监控、告警、诊断功能新增了对MongoDB的支持。 通过直观的方式将MongoDB多个维度的负载信息统一整合&#xff0c;不仅可以清晰的查看实时负载信息&#xff0c;也可以方…

在iview的Table中添加Select(render)

首先对Render进行分析&#xff0c;在iview官方的文档中&#xff0c;找到了table插入Button的例子&#xff1a; [javascript] view plaincopy { title: Action, key: action, width: 150, align: center, render: (h, params) > { return h(div, [ h(Butt…

工业机械人运动学正逆解,简单粗暴!!!!!!

ur机械臂是六自由度机械臂&#xff0c;由D-H参数法确定它的运动学模型&#xff0c;连杆坐标系的建立如上图所示。 转动关节θi是关节变量&#xff0c;连杆偏移di是常数。 关节编号 α&#xff08;绕x轴&#xff09; a&#xff08;沿x轴&#xff09; θ&#xff08;绕z轴&am…

python opencv立体测距 立体匹配BM算法

立体标定应用标定数据转换成深度图标定 在开始之前&#xff0c;需要准备的当然是两个摄相头&#xff0c;根据你的需求将两个摄像头进行相对位置的固定&#xff0c;我是按平行来进行固定的&#xff08;如果为了追求两个双目图像更高的生命度&#xff0c;也可以将其按一定钝角固…

Vue基础学习(一)------内部指令

一.v-if v-else v-show 指令 1.v-if v-if:是vue 的一个内部指令&#xff0c;指令用在我们的html中,用来判断是否加载html的DOM 现在举个栗子&#xff0c;判断用户的登录操作&#xff0c;用isLogin作为一个判断字段&#xff0c;登录成功&#xff0c;就显示用户的名称 代码&…

StereoRectify()函数定义及用法畸变矫正与立体校正

畸变矫正是上一篇博文的遗留问题&#xff0c;当畸变系数和内外参数矩阵标定完成后&#xff0c;就应该进行畸变的矫正&#xff0c;以达到消除畸变的目的&#xff0c;此其一。 在该系列第一部分的博文中介绍的立体成像原理中提到&#xff0c;要通过两幅图像估计物点的深度信息&a…

死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程

2019独角兽企业重金招聘Python工程师标准>>> 欢迎关注我的公众号“彤哥读源码”&#xff0c;查看更多源码系列文章, 与彤哥一起畅游源码的海洋。 删除元素 删除元素本身比较简单&#xff0c;就是采用二叉树的删除规则。 &#xff08;1&#xff09;如果删除的位置有两…

四元素理解

旋转变换_四元数 2017年03月29日 11:59:38 csxiaoshui 阅读数&#xff1a;5686 1.简介 四元数是另一种描述三维旋转的方式&#xff0c;四元数使用4个分量来描述旋转&#xff0c;四元数的描述方式如下&#xff1a; qsxiyjzk,(s,x,y,z∈ℝ&#xff09;i2j2k2ijk−1 四元数的由…

31、SAM文件中flag含义解释工具--转载

转载&#xff1a;http://www.cnblogs.com/nkwy2012/p/6362996.html SAM是Sequence Alignment/Map 的缩写。像bwa等软件序列比对结果都会输出这样的文件。samtools网站上有专门的文档介绍SAM文件。具体地址&#xff1a;http://samtools.sourceforge.net/SAM1.pdf很多人困惑SAM文…

《Head First设计模式》批注系列(一)——观察者设计模式

最近在读《Head First设计模式》一书&#xff0c;此系列会引用源书内容&#xff0c;但文章内容会更加直接&#xff0c;以及加入一些自己的理解。 观察者模式&#xff08;有时又被称为模型-视图&#xff08;View&#xff09;模式、源-收听者(Listener)模式或从属者模式&#xff…

PYPL 4 月排行:Python 最流行,Java 还行不行?

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; PYPL 发布了 4 月份的编程语言排行榜。 前五的分别是&#xff1a;Python、Java、Javascript、C# 和 PHP。可以看到&#xff0c;榜单没有什么大变化&#xff0c;但是相比去年 4 月份&#xff0c;…

顺序表

一、数据是如何在内存中存储的&#xff1f; 32位系统中char&#xff0c;int型数据在内存中的存储方式&#xff1a; char占1byte&#xff08;8bit&#xff09;int占4byte&#xff08;32bit&#xff09;假设我们有一个int类型的值&#xff0c;它从0x01开始&#xff0c;一个int占据…