为了账号安全,请及时绑定邮箱和手机立即绑定

如果函数在 Golang 中使用 mutex.lock() 被锁定,如何发送响应?

如果函数在 Golang 中使用 mutex.lock() 被锁定,如何发送响应?

Go
海绵宝宝撒 2022-09-05 17:06:01
我有这个功能。func (s *eS) Post(param *errorlogs.Q) (*errorlogs.Error, *errors.RestErr) {    //sub := q.Get("sub")    s.mu.Lock()    utime := int32(time.Now().Unix())    // Open our jsonFile    jsonFile, errFile := getlist(param.Id)    // if we os.Open returns an error then handle it    if errFile != nil {        return nil, errFile    }    jsonFile, err := os.Open(dir + "/File.json")    // if we os.Open returns an error then handle it    if err != nil {        return nil, errors.NewNotFoundError("Bad File request")    }    // read our opened jsonFile as a byte array.    byteValue, _ := ioutil.ReadAll(jsonFile)    // we initialize our  model    var errorFile errorlogs.Error_File    // we unmarshal our byteArray which contains our    // jsonFile's content into '' which we defined above    json.Unmarshal(byteValue, &errorFile)    // defer the closing of our jsonFile so that we can parse it later on    defer jsonFile.Close()    // An object to copy the required data from the response    var id int32    if len(errorFile.Error) == 0 {        id = 0    } else {        id = errorFile.Error[len(errorFile.Error)-1].ID    }    newValue := &errorlogs.Error{        ID:         id + 1,        Utime:      utime,        }    errorFile.Error = append(errorFile.Error, *newValue)    file, err := json.Marshal(errorFile)    if err != nil {        return nil, errors.NewInternalServerError("Unable to json marshal file")    }    err = ioutil.WriteFile(dir+"/File.json", file, 0644)    if err != nil {        return nil, errors.NewInternalServerError("Unable to write file")    }    s.mu.Unlock()    return newValue, nil}在这里,我从并发请求中锁定此函数,如果某个客户端已经写入文件,则不会让另一个客户端同时写入该文件。但现在我感到困惑,这个互斥体是什么。Lock() 在锁定时对所有其他请求执行操作?它是否让其他客户端等待?还是只是忽略所有其他客户端?我们有什么办法用某种回应发回客户端吗?或者让另一个客户端等待,然后允许他们访问此功能?
查看完整描述

1 回答

?
人到中年有点甜

TA贡献1895条经验 获得超7个赞

当互斥体被锁定时,对互斥锁()的所有其他调用都将被阻止,直到首先调用互斥锁()为止。

因此,当处理程序正在运行(并持有互斥锁)时,所有其他请求将在调用时被阻止。Lock()

注意:如果您的处理程序由于您提前返回(使用语句)而无法正常完成,或者它崩溃了,您的互斥锁将保持锁定状态,因此所有进一步的请求都将被阻止。return

一个好的做法是在互斥体被锁定后立即使用 defer 来解锁它:

s.mu.Lock()
defer s.mu.Unlock()

这确保了无论您的函数如何结束(可能正常结束,返回或恐慌),都将被调用。Unlock()

尝试尽可能少地保持锁定,以最大程度地减少其他请求的阻塞时间。虽然在进入处理程序时正确锁定并在返回之前仅解锁可能很方便,但如果在处理程序的“生存期”内不使用受保护的资源,则仅在使用共享资源时才锁定和解锁。例如,如果要保护对文件的并发访问,请锁定互斥锁,读/写文件,并在完成后立即解锁互斥锁。如何处理读取数据以及如何组装和发送响应不应阻止其他请求。当然,在使用解锁时,它可能不会像它应该的那样早运行(当您完成共享资源时)。因此,在某些情况下,可以使用 不使用 ,或者访问共享资源的代码可能会被移动到命名或未命名(匿名)函数,以便仍然能够使用 。deferdeferdefer

同步。互斥体不支持“扫视”状态,也不支持“尝试锁定”操作。这意味着使用时,您无法向客户端发出它必须等待的信号,因为处理请求正在等待另一个请求完成。如果您需要此类功能,则可以使用通道。容量为 1 的缓冲通道可以实现此功能:“锁定”操作在通道上发送值,“解锁”操作从通道接收值。目前为止,一切都好。“try-lock”操作可以是“有条件的”发送操作:使用带有事例的 select 语句,您可以检测到您现在无法锁定,因为它已被锁定,您可以执行其他操作或同时执行其他操作,并在以后重试锁定。sync.Mutexdefault

下面是一个示例:它可能看起来像这样:

var lock = make(chan struct{}, 1)


func handler(w http.ResponseWriter, r *http.Request) {

    // Try locking:

    select {

    case lock <- struct{}{}:

        // Success: proceed

        defer func() { <-lock }() // Unlock deferred

    default:

        // Another handler would block us, send back an "error"

        http.Error(w, "Try again later", http.StatusTooManyRequests)

        return

    }


    time.Sleep(time.Second * 2) // Simulate long computation

    io.WriteString(w, "Done")

}


func main() {

    http.HandleFunc("/", handler)

    log.Fatal(http.ListenAndServe(":8080", nil))

}

上面的简单示例如果另一个请求持有锁定,则会立即返回错误。你可以选择在这里做不同的事情:你可以把它放在一个循环中,在放弃并返回错误之前重试几次(在迭代之间稍微睡觉)。您可以在尝试锁定时使用超时,并且只有在一段时间内无法获得锁定时才接受“失败”(请参阅时间。After() 和上下文。WithTimeout())。当然,如果我们使用某种超时,则必须删除该案例(如果其他案例都不能立即进行,则立即选择该案例)。defaultdefault


当我们处于它(超时)时,由于我们已经在使用 ,因此我们可以合并监视请求的上下文是一个好处:如果它被取消,我们应该提前终止并返回。为此,我们可以通过添加从上下文的 done 通道接收的案例来执行此操作,例如 。selectcase <-r.Context().Done():


下面是一个示例,如何简单地使用:select


var lock = make(chan struct{}, 1)


func handler(w http.ResponseWriter, r *http.Request) {

    // Wait 1 sec at most:

    ctx, cancel := context.WithTimeout(r.Context(), time.Second) 

    defer cancel()


    // Try locking:

    select {

    case lock <- struct{}{}:

        // Success: proceed

        defer func() { <-lock }() // Unlock deferred

    case <-ctx.Done():

        // Timeout or context cancelled

        http.Error(w, "Try again later", http.StatusTooManyRequests)

        return

    }


    time.Sleep(time.Second * 2) // Simulate long computation

    io.WriteString(w, "Done")

}


查看完整回答
反对 回复 2022-09-05
  • 1 回答
  • 0 关注
  • 99 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信