VB.net学习笔记二十八线程同步下


3、ReaderWriterLock 类
ReaderWriterLock定义了实现单写程序和多写程序语义的锁。ReaderWriterLock类中4个主要的方法
• AcquireReacJerLock():获得-个读程序锁,超时值使用一个整数或一个 TimeSpan。
• AcquireWiiterLock(): 获得一个写程序锁,超时值使用一个整数或一个 TimeSpan。
• ReleaseReaderLock():释放读程序锁。
• ReleaseWriterLock(): 释放写程序锁。
一个线程可以持有读线程锁或写线程锁,但是不能同时持有两者。

Imports System.Threading
Namespace AReadWriteLock
    Public Class ReadWrite
        Private rwl As ReaderWriterLock
        Private x As Integer
        Private y As Integer
        Public Sub New()
            rwl = New ReaderWriterLock()
        End Sub
        Public Sub ReadInts(ByRef a As Integer,ByRef b As Integer)
            rwl.AcquireReaderLock(Timeout.Infinite)
            Try
                a = x
                b = y
            Finally
                rwl.ReleaseReaderLock()
            End Try
        End Sub
        Public Sub WriteInts(ByVal a As Integer,ByVal b As Integer)
            rwl.AcquireWriterLock(Timeout.Infinite)
            Try
                x = a
                y = b
                Console.WriteLine(" x=" & x & " y=" & y & " ThreadID=" & Thread.CurrentThread.GetHashCode.ToString)
            Finally
                rwl.ReleaseWriterLock()
            End Try
        End Sub
    End Class
    Public Class RWApp
        Private rw As New ReadWrite
        Public Overloads Shared Sub Main(ByVal args() As String)
            Dim e As New RWApp()
            Dim wt1 As New Thread(New ThreadStart(AddressOf e.Write))
            wt1.Start()
            Dim wt2 As New Thread(New ThreadStart(AddressOf e.Write))
            wt2.Start()
            Dim rt1 As New Thread(New ThreadStart(AddressOf e.Read))
            rt1.Start()
            Dim rt2 As New Thread(New ThreadStart(AddressOf e.Read))
            rt2.Start()
            Console.ReadLine()
        End Sub
        Private Sub Write()
            Dim a As Integer = 10
            Dim b As Integer = 11
            Console.WriteLine("=== Write ID:" & Thread.CurrentThread.GetHashCode.ToString)
            For i As Integer = 0 To 2
                rw.WriteInts(a,b)
                a += 1
                b += 1
                Thread.Sleep(1000)
            Next i
        End Sub
        Private Sub Read()
            Dim a As Integer = 10
            Dim b As Integer = 11
            Console.WriteLine("=== Read ID:" & Thread.CurrentThread.GetHashCode.ToString)
            For i As Integer = 0 To 2
                rw.ReadInts(a,b)
                Console.WriteLine("For i=" & i & " a=" & a & ” b=” & b & " ThreadID=" & Thread.CurrentThread.GetHashCode.ToString)
                Thread.Sleep(1000)
            Next i
        End Sub
    End Class
End Namespace
线程的读锁或写锁同一时间只能有一个进入,结果如下:


(三)手控同步
System.Threading命名空间的一些可以用做手控同步的类。它们赋予了程序员使用类似于WIN32线程API的低级线程API创建和管理多线程应用程序的能力。如:Auto ResetEvent类、ManualResetEvent类、Mutex类、Interlocked类。
1、ManualResetEvent类
通知一个或多个正在等待的线程已发生事件,根据这个信号正在等待的线程决定是否继续向下运行。ManualResetEvent对象只能拥有两种状态:有信号(True)或无信号(False)。
ManualResetEvent 就象灯塔的信号灯,作用是阻塞一个或多个线程,直到收到一个信号告诉ManualResetEvent不要再阻塞当前的线程。
如果有信号,船只一路畅通无阻,当前线程勇往直前运行,即使遇WaitOne也继续前行;
如果无信号,船只一路遇礁则止,当前线程遇WaitOne则挂起受阻。

Imports System.Threading
Namespace NETThreadEvents
    Class AManualReset
        Shared LightSign As New ManualResetEvent(False) '1、无信号
        Shared Sub main()
            Dim t(4) As Thread
            For i As Integer = 0 To 3
                t(i) = New Thread(AddressOf RunOrWait)
                t(i).Start()
            Next
            'LightSign.Set()                '3、恢复有信号,线程甩开阻碍继续畅通向前运行
            Console.Read()
        End Sub
        Public Shared Sub RunOrWait()
            Console.WriteLine("ID " & Thread.CurrentThread.GetHashCode.ToString & " waiting... ")
            LightSign.WaitOne()                  '2、无信号时线程挂起受阻,有信号则畅通
            Console.WriteLine("ID " & Thread.CurrentThread.GetHashCode.ToString & "Running... not blocked!")
        End Sub
    End Class
End Namespace
说明:左图:1处设置为False,指示线程遇WaitOne受阻挂起,所以在RunOrWait()方法(2处)的WaitOne时就挂起了。中图:1处设置为True,线程畅通,2处WaiOne直接通过。右图:1处为false无信号,所以在2处受阻,但是由于3处加了一句Set,让信号亮起,前面挂起的恢复通过,没挂起当然也通过。图中可以看出10-12线程受阻,由于主线程Set恢复信号后,原挂起的11、12线程继续跑起,至于谁抢到CPU由OS决定。

注意:至于终结与未终结,MSDN:
原文:Reset Sets the state of the event to non-signaled,which causes threads to block. (Inherited from EventWaitHandle.)
原译文:Reset 将事件状态设置为非终止状态,从而导致线程受阻。 (从 EventWaitHandle 继承。)
因为non-signaled被翻译成了未终止状态。把受阻线程设置成无信号事件状态。

2、AutoResetEvent类
通知正在等待的线程已发生事件。处于等待状态的线程,直到通过调用Set()方法将它置于信号通知状态。
AutoResetEvent和ManualResetEvent是类似的。但有少些区别。
AutoResetEvent和ManualResetEvent的区别:
(1) AutoResetEvent只会给一个线程发送信号(非全部)。ManualResetEvent给全部线程发信号。
(2) AutoResetEvent在set()后,会将线程状态自动置为false。ManualResetEvent在Set()后,线程的状态就变为true,必须手动ReSet()之后,才会重新将线程置为false。
所以ManualResetEvent就象大门(Sign)一打开(True),所有马(Threads)都跑了,且门一直开启True。AutoResetEvent只随机开一匹的门(Set),放跑(True)后马上关闭(False)。
Imports System.Threading
Namespace NETThreadEvents
    Class AManualReset
        Shared LightSign As New AutoResetEvent(False) '1、无信号
        Shared Sub main()
            Dim t(4) As Thread
            For i As Integer = 0 To 3
                t(i) = New Thread(AddressOf RunOrWait)
                t(i).Start()
            Next
            LightSign.Set()     '2,随机释放某一已经阻塞的线程(不是全部)
            Console.Read()
        End Sub
        Public Shared Sub RunOrWait()
            Console.WriteLine("ID " & Thread.CurrentThread.GetHashCode.ToString & " waiting... ")
            LightSign.WaitOne()
            Console.WriteLine("ID " & Thread.CurrentThread.GetHashCode.ToString & "Running... not blocked!")
        End Sub
    End Class
End Namespace
说明:Set后,仅将一个线程变成True,所以 ID9线程通过了,ID12仍然阻塞.


3、Mutex类(mutex互斥)
Mutex锁提供了交叉线程和交叉同步进程。如果没有线程拥有有信号状态,Mutex的状态就会被置为有信号状态。Mutex并不具有Monitor类的所有等待和脉冲功能,不过它确实提供了能够在进程之间使用的命名互斥体(使用重载 构造函数)的创建。使用Mutex类优于Monitor类的是:Mutex类可跨进程使用,而Monitor类则不行。
Mutex(boolean initiallyOwned,string name) 初始化 Mutex 类的新实例。
initiallyOwned: 若为 true,调用端拥有互斥体的初始所属权;否则为 false。
Name: Mutex 的名称。如果值为 null,则 Mutex 是未命名的。
Imports System.Threading
Namespace AMutex
    Class NETMutex
        Private Shared myMutex As Mutex
        Public Shared Sub Main()
            myMutex = New Mutex(True,"Magic")
            Dim nm As New NETMutex
            Dim t As New Thread(New ThreadStart(AddressOf nm.Run))
            t.Start()


            Dim ID As String = Thread.CurrentThread.GetHashCode.ToString
            Console.WriteLine("ID:" & ID & " main thread will sleep for 3 seconds...")
            Thread.Sleep(3000)
            Console.WriteLine("ID:" & ID & ” main thread Woke Up")

            myMutex.ReleaseMutex()  '1、释放互斥锁(让线程t中3处得到锁,以便向下运行)
            Console.WriteLine("ID:" & ID & " main Before WaitOne")
            myMutex.WaitOne()       '2、阻塞,直到再次得到互斥锁(线程t中4处释放锁)
            Console.WriteLine("ID:" & ID & " main Lock. owned by Main Thread")
            Console.ReadLine()
        End Sub
        Public Sub Run()
            Dim ID As String = Thread.CurrentThread.GetHashCode.ToString
            Console.WriteLine("ID:" & ID & " t_thread In Run method")
            myMutex.WaitOne()       '3、阻塞线程t,直到得到互斥锁),1外释放后,这里通畅向下
            Console.WriteLine("ID:" & ID & " t_thread will sleep for 6 seconds")
            Thread.Sleep(6000)

            Console.WriteLine("ID:" & ID & " t_thread end of Run method")
            myMutex.ReleaseMutex()  '4、此处必须释放锁,否则2处会无限等待锁(最后抛出异常)
        End Sub
    End Class
End Namespace
说明: 主线程创建互斥锁后,拥有该锁,等待3秒后在1处释放锁,以便让线程t拥有锁后(3处)继续向下运行,直到释放锁(4处),释放后主线程马上得到锁并在阻塞的2处继续向下运行。4处必须释放原因:MSDN: 在桌面的 .NET 中,如果没有线程拥有互斥体,则互斥体的状态为终止并将在下一个获取互斥体的线程中引发 AbandonedMutexException。


4、Intelocked类
Interlocked类为原子操作,在多个线程之间共享的非阻塞整数更新提供了方法。如果变量位于共享的内存中的话,不同进程的线程以使用这种机制。
原子操作,即不可分割操作,一个线程操作时,不会切换到另一个操作。可简单理解为独占模式。

Interlocked可以为多个线程共享的变量提供原子操作:
Interlocked.Increment:以原子操作的形式递增指定变量的值并存储结果。
Interlocked.Decrement 以原子操作的形式递减指定变量的值并存储结果。
Interlocked.Add 以原子操作的形式,添加两个整数并用两者的和替换第一个整数

注意:Intelocked原子操作锁定的是某一个值,仅在类似上面三句中生效,越过此句原子操作失效。

Imports System.Threading
Namespace AInterLocker
    Class WinterLocked
        Public a As New ManualResetEvent(False)
        Private i As Integer = 5
        Public Sub Run(ByVal s As Object)
            Interlocked.Increment(i)   '2、原子操作递增
            Console.WriteLine(Thread.CurrentThread.GetHashCode.ToString & "   " & i)
            Thread.Sleep(500)
            'Console.WriteLine(Thread.CurrentThread.GetHashCode.ToString & " completed")  ‘3、非原子操作
        End Sub
        Public Function GetValue() As Integer
            Return i
        End Function
    End Class
    Public Class MainApp
        Public Shared Sub Main()
            Dim mR As New ManualResetEvent(False)
            Dim wL As New WinterLocked

            For i As Integer = 1 To 10   '1、线程池排队
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf wL.Run),2)
            Next i
            mR.WaitOne(3000,True)       '在3秒后退出同步域
            Console.WriteLine("Result of 10 times is " & wL.GetValue)
            Console.ReadLine()
        End Sub
    End Class
End Namespace
说明:下面左右两图都是上面的结果,特别是左图,为啥会出现两个11,而少了10呢?首先确认10个线程进入后是进行了原子操作,每个线程增加1,因为10个线程的结果都是15(=5+10),因为原子操作仅在2处,过了此句后面的都不是原子操作。细节为:A线程原子锁定i(=9),然后增加1(i=10),后而退出原子操作马上要执行后一句提取i值(但还没显示),这里B线程切换进来,原子锁定i并对i增加1(i=10+1=11),然后原子操作退出并提取i值到B线程中。注意A、B线程提取的i值都是11,故显示的都是11,至于中间的12则是在输出过程中一样是要花费时间的,被另一线程C切入并提取i值12,并于B线程前输出。在3处增加一句显示i的结果,上面的效果将更加明显。


5、共享变量、方法和同步
共享(Shared)的变量和方法,既可以被类访问也可被该类的实例访问,所以同步锁定Shared的变量或方法就被应用到整个该类上。此时,其它对象不允许使用此类的Shared变量或方法。

ThreadStaticAttribute 类
带有ThreadStaticAttribute 的Shared变量,对每个访问变量的线程都会有一个同一变量的单独副本。意味着如果一个线程修改了变量,另一个访问变量的线程就不能看到这些变化(包括主线程)。这种行为是有背于Shared变量的默认行为的。用处比如web应用中,每个请求都是一个独立的线程,如果我们希望将一个值作为静态字段全局使用,同时又不想影响其他用户,这时候一般我们是使用Session的。

Imports System.Threading
Namespace TestShared
    Class AThreadStatic
        <ThreadStatic> Public Shared x As Integer  '1、共享变量,各线程分别有副本x
        Public Shared y As Integer = 1             '2、共享变量,各线程无副本
        Public Sub Run()
            For i As Integer = 1 To 5
                Dim id As String = Thread.CurrentThread.GetHashCode.ToString
                x += 1
                y += 1
                Console.WriteLine("i=" & i & “ ThreadID=" & id & " x=" & x & " y=" & y)
                Thread.Sleep(1000)                 '3.共5次循环,花时5秒
            Next i
        End Sub
    End Class
    Public Class MainApp
        Public Shared Sub Main()
            Dim tS As New AThreadStatic
            Dim tl As New Thread(New ThreadStart(AddressOf tS.Run))
            Dim t2 As New Thread(New ThreadStart(AddressOf tS.Run))
            tl.Start()
            t2.Start()
            Thread.Sleep(3500)                     '4、在3.5秒后,看一下类中共享变量值
            Console.WriteLine("Main thread get value1:" & AThreadStatic.x & " " & AThreadStatic.y)
            Thread.Sleep(4000)                     '5、又4秒后,看一下类中共享变量值
            Console.WriteLine("Main thread get value2:" & AThreadStatic.x & " " & AThreadStatic.y)
            Console.ReadLine()
        End Sub
    End Class
End Namespace
说明:x被标注后,在两个线程中将分别生成各自的x的副本(并不影响原类的值),所以在4处可以看到原类中x仍为0,但y没被标注,所以值是变化的。最后的结果(5处),也可以看出始终x没被修改为0(仅在各自的线程中修改其副本),y是修改的为11。


(四)防止死锁
线程越多,上锁越复杂,就越容易死锁。通常的防止的原则就是:一个线程最多只能有一个锁。
如果想更多的锁,就越容易死锁。例如:A线程中拥有锁L1期待锁L2;而B线程中拥有锁L2期待锁L1;如果两个同时发生或重叠,就会发生死锁。当然如果时间错过也就无所谓。
Imports System.Threading
Namespace DeadLock
    Class DL
        Private field_1 As Integer = 0
        Private field_2 As Integer = 0
        Private lock_1 As Object = New Integer(1) {}
        Private lock_2 As Object = New Integer(1) {}
        Public Sub first(ByVal val As Integer)
            SyncLock lock_1
                Console.WriteLine("First:Acquired lock_1:" & Thread.CurrentThread.GetHashCode.ToString + " Now Sleeping")
                Thread.Sleep(1000)
                SyncLock lock_2
                    Console.WriteLine("First:Acquired lock_2:" & Thread.CurrentThread.GetHashCode.ToString)
                    field_1 = val
                    field_2 = val
                End SyncLock
            End SyncLock
        End Sub
        Public Sub second(ByVal val As Integer)
            SyncLock lock_2
                Console.WriteLine("Second:Acquired lock_2:" & Thread.CurrentThread.GetHashCode.ToString)
                SyncLock lock_1
                    Console.WriteLine("Second:Acquired lock I:" & Thread.CurrentThread.GetHashCode().ToString)
                    field_1 = val
                    field_2 = val
                End SyncLock
            End SyncLock
        End Sub
    End Class
    Public Class MainApp
        Private d As New DL()
        Public Shared Sub Main()
            Dim m As New MainApp
            Dim tl As New Thread(New ThreadStart(AddressOf m.Run1))
            tl.Start()
            Dim t2 As New Thread(New ThreadStart(AddressOf m.Run2))
            t2.Start()
            Console.ReadLine()
        End Sub
        Public Sub Run1()
            d.first(10)
        End Sub
        Public Sub Run2()
            d.second(10)
        End Sub
    End Class
End Namespace
说明:两个线程都分别拥有锁,还期待对方向的锁,都处于等待对方的锁,于是就死锁了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强制返回为文本 -------------------------------- 数字类型的格式化 --------------------------------     固定格式参数:     General Number 普通数字,如可以用来去掉千位分隔号     format$("100,1
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办法, Format 或者FormatDateTime 竟然结果和系统设置的区域语言的日期和时间格式相关。意思是尽管你用诸如 Format(Now, "MM/dd/yyyy"),如果系统的设置格式区域语言的日期和时间格式分隔符是"-",那他还会显示为 MM-dd-yyyy     只有拼凑: <%response.write
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace My ‘全局错误处理,新的解决方案直接添加本ApplicationEvents.vb 到工程即可 ‘添加后还需要一个From用来显示错误。如果到这步还不会则需要先打好基础啦 ‘======================================================== ‘以下事件
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用的爽呀,这篇文章写与2011年,看来我以前没有认真去找这个方法呀。 https://blog.csdn.net/chzjxgd/article/details/6176325 金蝶K3 BOS的插件官方是用VB6编写的,如果  能用.Net下的语言工具开发BOS插件是一件很愉快的事情,其中缘由不言而喻,而本文则是个人首创,实现在了用V
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选中的单元格进行处理 Dim m As Range, tmpStr As String, s As String Dim x As Integer, y As Integer, subStr As String If MsgBox("确定要分列处理吗?请确定分列的数据会覆盖它后面的单元格!", _
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) 2 Dim path As String, hash As String 3 For Each fil
  Imports MySql.Data.MySqlClient Public Class Form1 ‘ GLOBAL DECLARATIONS Dim conString As String = "Server=localhost;Database=net2;Uid=root;Pwd=123456;" Dim con As New MySqlConnection
‘導入命名空間 Imports ADODB Imports Microsoft.Office.Interop   Private Sub A1() Dim Sql As String Dim Cnn As New ADODB.Connection Dim Rs As New ADODB.Recordset Dim S As String   S = "Provider=OraOLEDB.Oracl
Imports System.IO Imports System.Threading Imports System.Diagnostics Public Class Form1 Dim A(254) As String    Function ping(ByVal IP As Integer) As String Dim IPAddress As String IPAddress = "10.0.
VB运行EXE程序,并等待其运行结束 参考:https://blog.csdn.net/useway/article/details/5494084 Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Pr
今天碰到一个问题,登陆的时候,如果不需要验证手机号为空,则不去验证手机号 因为登陆的时候所有的验证信息都存放在一个数组里 Dim CheckUserInfo() As String ={UserBirthday, SecEmail, UserMob, UserSex, RealNameFirst, RealName, CheckCardID, CheckCardType, Contactemail
在VB6.0中,数据访问接口有三种: 1、ActiveX数据对象(ADO) 2、远程数据对象(RDO) 3、数据访问对象(DAO) 1.使用ADO(ActiveX Data Objec,ActiveX数据对象)连接SQL Server 1)使用ADO控件连接 使用ADO控件的ConnectionString属性就可以连接SQL Server,该属性包含一个由分号分隔的argument=value语
注:大家如果没有VB6.0的安装文件,可自行百度一下下载,一般文件大小在200M左右的均为完整版的软件,可以使用。   特别提示:安装此软件的时候最好退出360杀毒软件(包括360安全卫士,电脑管家等,如果电脑上有这些软件的话),因为现如今的360杀毒软件直接会对VB6.0软件误报,这样的话就可能会在安装过程中被误报阻止而导致安装失败,或者是安装后缺乏很多必须的组件(其它的杀毒软件或安全卫士之类的
Private Sub Form_Load() Call conndb End Sub Private Function conndb() Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Dim strCn, sql As String Dim db_host As String Dim db_user As String
  PPSM06S70:  Add  moddate  EDITSPRINTJOB:  MAX(TO_CHAR(ETRN.MODDATE, ‘yyyy/mm/dd/HH24:MI AM‘)) ACTUAL_SHIPDATE   4.Test Scenario (1) :Query SQL Test DN:8016578337 SELECT CTRN.TKCTID TRUCK_ID,        
  沒有出現CrystalReportViewer時,須安裝CRforVS_13_0. 新增1個數據集,新增1個數據表,添加二列,列名要和資料庫名一樣. 修改目標Framework 修改app.config, <startup >改成<startup useLegacyV2RuntimeActivationPolicy ="true">  CrystalReport1.rpt增加數據庫專家 在表單
Imports System.Threading Imports System Public Class Form1 Dim th1, th2 As Thread Public Sub Method1() Dim i As Integer For i = 1 To 100 If Me.Label1.BackColor =
Friend Const PROCESS_ALL_ACCESS = &H1F0FFF = 2035711 Friend Const PROCESS_VM_READ = &H10 Friend Const PROCESS_VM_WRITE = &H20 Friend Const PAGE_READONLY = &H2 Friend Const PAGE_READWRITE = &H4 Friend
以下代码随手写的 并没有大量测试 效率也有待提升 如果需要C#的请自行转换 Function SplitBytes(Data As Byte(), Delimiter As Byte()) As List(Of Byte()) Dim i = 0 Dim List As New List(Of Byte()) Dim bytes As New
Imports System.Data.SqlClient Public Class Form1 REM Public conn1 As SqlConnection = New SqlConnection("server=.; Integrated Security=False;Initial Catalog= mydatabase1; User ID= sa;password")