线程呢在使用的时候呢会带来一系列问题，那其中一个呢
如果我们多个线程呢都去访问同一个集合，那这个时候呢
也会带来一系列问题，那么比较好的一个办法呢就是使用这些 集合，比如包括 Array ,ArrayList,
SortedList 等等，这些呢它都有一个 Synchronized
的这个方法，获取一个呢线程安全 的这样的一个包装对象，就使用获得的这个包装的这个对象
那它就是比较安全的，当然这会带来，由于线程呢同时访问的话，会带来一些效率的问题， 我们看这个例子，这里呢有一个
ArrayList 对象 这个 ArrayList 对象呢它有一个叫
Synchronized 的这个方法 可以把我们普通的这样一个 ArrayList
的一个集合 包装成一个呢线程安全的集合，然后我们在线程里面去访问它
就比较好了，这里面呢，这个集合呢它有一个是否是线程安全的属性
跟线程使用呢另一个相关的问题就是 Windows 界面，经常我们就要
编写程序就是在比如说一个按钮点击了，然后我们就要做一系列复杂的事情 那这个复杂的事情呢经常我们把它放到一个线程里去做
那线程的这些事情做了以后呢，我们需要比如说它的运算，运算完了以后呢，我们在这个- 线程里面
又要把界面上比如说，标签上的信息给重新显示一下 那么也就是说，线程里面它需要更
新界面，那这个时候呢 framework 2.0 以上呢 就考虑到这个问题，因为我们多个线程可能都要去更新界面
那这个时候就可能带来这个界面显示 跟我们实际的那个要的信息呢是不一样的，规定呢只能
主线程，当然这个主线程呢我们实际上是界面线程，那我们简称为主线程
只有界面的这个线程呢才能对界面进行更新，或者获取 界面的内容，比如说取得文本框的文本，刚才那种情况我们在别的线程里面
又要访问主线程怎么办呢？就给了一个解决办法，判断 如果是
InvokeRequired，就是需要用调用这种方式 那我们就要用
BeginInvoke 这个方法 而不能直接访问这个方法，比如说我们要在那个
信息要加到一个那个列表框里面，那普通的情况，如果在主线程我们直接可以调 但是如果是要求
required， 也就是在别的线程要访问它的话
那我们就必须用 BeginInvoke，实际上就相当于用试图的或者说
我开始调用开始做某个事情，当然这个呢把我们要做的事情呢 放到主线程里的那里的队那里头去
它来调动进行处理，由于 BeginInvoke 需要一个 delegate
需要一个委托，所以我们写起来比较啰嗦，当然好在现在我们有 匿名函数呢有 Lambda
表达式啊，相对来说呢用起来还方便点 这里面呢就是你一定要定义一个，定义一个委托，然后把你要调用的函数呢
放到委托里头，才能用 BeginInvoke，这个函数的一些参数我们可以用一个
数组传进去，下面我们看看这个代码，当我们一个按钮点击了以后 然后比如说我们要做某件事情，是
MyFun，它是一个 比如说是一个无限的循环，或者一个耗时间的操作
然后这个呢耗时间的操作呢我们就把它放到一个 thread 对象里头，那这个
thread.Start 启动这个对象 在这个 thread
里头呢，做了某一些事情以后呢，我们需要显示信息 这个显示信息我们不能直接用这个界面对象
AddMsgFun，直接把那个列表框里面的 Items 怎么处理，我们不能直接这么调
因为它是在线程里调界面的方法呢，一定要判断一下 InvokeRequired
然后我们用 BeginInvoke，然后再去把这个函数呢装成一个 Delegate
这个 delegate 呢当然我们就要定义这个类型了，这个类型呢你的自定义是这样一个
delegate 然后它带一个参数，返回为 void，然后这个参数呢通过 new
object 这样一个对象数组啊把这个参数传给
委托，总的说来呢，界面里面呢如果是线程 你必须用 delegate，然后呢用
BeginInvoke 的方式，不然的话我们会引起这个界面呢
混乱，更新界面呢由于比较麻烦，所有后来呢 C# 就想了一些办法，比如说
有一个叫 BackgroundWorker 这个组件，它里面呢有一系列的方法和事件，其中最重要的呢就是
RunWorkerAsync，就是异步地做，异步地做某件事情
它这里面呢，然后这里面有个事件就是真正做的时候呢
我们可以呢，直接访问界面里的对象，使用起来呢
稍微好一点点，这个组件里面你可以直接写访问界面，我觉得这个组件用起来呢
还是相当麻烦，对大家来说呢有个好消息，就是 C#
5.0 以上呢 我们可以用更方便，这就是 C#
5.0 呢增加的 Await 异步的方法，我们后面会讲到