与runtime.LockOSThread()和runtime.UnlockOSThread有问题

huangapple go评论69阅读模式
英文:

Issue with runtime.LockOSThread() and runtime.UnlockOSThread

问题

我有一个像这样的代码,

例程1 {
runtime.LockOSThread()
打印一些内容
发送整数给例程2
runtime.UnlockOSThread

}

例程2 {
runtime.LockOSThread()
打印一些内容
发送整数给例程1
runtime.UnlockOSThread

}


主函数 {
启动例程1
启动例程2
}

我使用运行时锁定-解锁是因为我不希望例程1的打印与例程2混合。然而,在执行上述代码后,输出与没有锁定-解锁时相同(即打印输出混合)。有人可以帮我解释为什么会发生这种情况,以及如何强制防止这种情况发生。

注:我给出了一个打印内容的示例,但实际上有很多打印和发送事件。

英文:

I have a code like,

Routine 1 {
runtime.LockOSThread()
print something
send int to routine 2
runtime.UnlockOSThread

}

Routine 2 {
runtime.LockOSThread()
print something
send int to routine 1
runtime.UnlockOSThread

}


main {
go Routine1
go Routine2
}

I use run time lock-unlock because, I don't want that printing of
Routine 1 will mix with Routine 2. However, after execution of above
code, it outputs same as without lock-unlock (means printing outputs
mixed). Can anybody help me why this thing happening and how to force
this for happening.

NB: I give an example of print something, however there are lots of
printing and sending events.

答案1

得分: 3

如果你想要序列化“打印一些东西”,例如每个“打印一些东西”都应该是原子性的,那么只需将其包围在互斥锁中即可。

你可以通过互斥锁来包围“打印一些东西”。除非由于此原因导致代码死锁,否则这将起作用 - 在非平凡的程序中,这很容易发生。

在Go中,序列化某些内容的简单方法是使用通道。在一个(go)协程中收集应该一起打印的所有内容。当打印单元的收集完成后,通过通道将其作为“打印作业”单元发送给某个打印“代理”。该代理将简单地接收其“任务”并原子地打印每个任务。这样可以免费获得原子性,并且在简单情况下,代码不容易死锁,其中只有非相互依赖的“打印单元”生成的goroutine。

我的意思是这样的:

func printer(tasks chan string) {
        for s := range tasks {
                fmt.Printf(s)
        }
}

func someAgentX(tasks chan string) {
        var printUnit string
        //...
        tasks <- printUnit
        //...
}

func main() {
        //...
        tasks := make(chan string, size)
        go printer(tasks)
        go someAgent1(tasks)
        //...
        go someAgentN(tasks)
        //...
        <- allDone
        close(tasks)
}
英文:

If you want to serialize "print something", e.g. each "print something" should perform atomically, then just serialize it.

You can surround "print something" by a mutex. That'll work unless the code deadlock because of that - and surely it easily can in a non trivial program.

The easy way in Go to serialize something is to do it with a channel. Collect in a (go)routine everything which should be printed together. When collection of the print unit is done, send it through a channel to some printing "agent" as a "print job" unit. That agent will simply receive its "tasks" and atomically print each one. One gets that atomicity for free and as an important bonus the code can not deadlock easily no more in the simple case, where there are only non interdependent "print unit" generating goroutines.

I mean something like:

func printer(tasks chan string) {
        for s := range tasks {
                fmt.Printf(s)
        }
}

func someAgentX(tasks chan string) {
        var printUnit string
        //...
        tasks &lt;- printUnit
        //...
}

func main() {
        //...
        tasks := make(chan string, size)
        go printer(tasks)
        go someAgent1(tasks)
        //...
        go someAgentN(tasks)
        //...
        &lt;- allDone
        close(tasks)
}

答案2

得分: 1

runtime.LockOSThread的作用是阻止其他goroutine在同一个线程上运行。它强制运行时创建一个新的线程并在那里运行Routine2。它们仍然并发运行,但在不同的线程上。

你需要使用sync.Mutex或一些通道魔法来代替。

你很少需要使用runtime.LockOSThread,但它可以用于强制某些优先级较高的goroutine在自己的线程上运行。

package main

import (
	"fmt"
	"sync"
	"time"
)

var m sync.Mutex

func printing(s string) {
	m.Lock() // 如果m未解锁,其他goroutine将在此处停止
	fmt.Println(s)
	m.Unlock() // 现在另一个在“m.Lock()”处的goroutine可以继续运行
}

func main() {
	for i := 0; i < 10; i++ {
		go printing(fmt.Sprintf("Goroutine #%d", i))
	}
	
	<-time.After(3e9)
}
英文:

What runtime.LockOSThread does is prevent any other goroutine from running on the same thread. It forces the runtime to create a new thread and run Routine2 there. They are still running concurrently but on different threads.

You need to use sync.Mutex or some channel magic instead.

You rarely need to use runtime.LockOSThread but it can be useful for forcing some higher priority goroutine to run on a thread of it's own.

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

var m sync.Mutex

func printing(s string) {
	m.Lock() // Other goroutines will stop here if until m is unlocked
	fmt.Println(s)
	m.Unlock() // Now another goroutine at &quot;m.Lock()&quot; can continue running
}

func main() {
	for i := 0; i &lt; 10; i++ {
		go printing(fmt.Sprintf(&quot;Goroutine #%d&quot;, i))
	}
	
	&lt;-time.After(3e9)
}

答案3

得分: 0

我认为,这是因为runtime.LockOSThread()和runtime.UnlockOSThread()并不总是起作用。它完全取决于CPU、执行环境等因素。无法通过其他方式强制执行。

英文:

I think, this is because of runtime.LockOSThread(),runtime.UnlockOSThread does not work all time. It totaly depends on CPU, execution environment etc. It can't be forced by anyother way.

huangapple
  • 本文由 发表于 2011年12月5日 03:46:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/8378092.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定