专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

Golang58个坑中级篇3651

  36。关闭HTTP的响应体37。关闭HTTP连接38。将JSON中的数字解码为interface类型39。struct、array、slice和map的值比较40。从panic中恢复41。在range迭代slice、array、map时通过更新引用来更新元素42。slice中隐藏的数据43。Slice中数据的误用44。旧slice45。类型声明与方法46。跳出forswitch和forselect代码块47。for语句中的迭代变量与闭包函数48。defer函数的参数值49。defer函数的执行时机50。失败的类型断言51。阻塞的gorutinue与资源泄露36。关闭HTTP的响应体
  使用HTTP标准库发起请求、获取响应时,即使你不从响应中读取任何数据或响应为空,都需要手动关闭响应体。新手很容易忘记手动关闭,或者写在了错误的位置:请求失败造成panicfuncmain(){resp,err:http。Get(https:api。ipify。org?formatjson)deferresp。Body。Close()resp可能为nil,不能读取Bodyiferr!nil{fmt。Println(err)return}body,err:ioutil。ReadAll(resp。Body)checkError(err)fmt。Println(string(body))}funccheckError(errerror){iferr!nil{log。Fatalln(err)}}
  上边的代码能正确发起请求,但是一旦请求失败,变量resp值为nil,造成panic:
  panic:runtimeerror:invalidmemoryaddressornilpointerdereference
  应该先检查HTTP响应错误为nil,再调用resp。Body。Close()来关闭响应体:大多数情况正确的示例funcmain(){resp,err:http。Get(https:api。ipify。org?formatjson)checkError(err)deferresp。Body。Close()绝大多数情况下的正确关闭方式body,err:ioutil。ReadAll(resp。Body)checkError(err)fmt。Println(string(body))}
  输出:Gethttps:api。ipify。org?format。。。:x509:certificatesignedbyunknownauthority
  绝大多数请求失败的情况下,resp的值为nil且err为nonnil。但如果你得到的是重定向错误,那它俩的值都是nonnil,最后依旧可能发生内存泄露。2个解决办法:可以直接在处理HTTP响应错误的代码块中,直接关闭非nil的响应体。手动调用defer来关闭响应体:正确示例funcmain(){resp,err:http。Get(http:www。baidu。com)关闭resp。Body的正确姿势ifresp!nil{deferresp。Body。Close()}checkError(err)deferresp。Body。Close()body,err:ioutil。ReadAll(resp。Body)checkError(err)fmt。Println(string(body))}
  resp。Body。Close()早先版本的实现是读取响应体的数据之后丢弃,保证了keepalive的HTTP连接能重用处理不止一个请求。但Go的最新版本将读取并丢弃数据的任务交给了用户,如果你不处理,HTTP连接可能会直接关闭而非重用,参考在Go1。5版本文档。
  如果程序大量重用HTTP长连接,你可能要在处理响应的逻辑代码中加入:,errio。Copy(ioutil。Discard,resp。Body)手动丢弃读取完毕的数据
  如果你需要完整读取响应,上边的代码是需要写的。比如在解码API的JSON响应数据:json。NewDecoder(resp。Body)。Decode(data)37。关闭HTTP连接
  一些支持HTTP1。1或HTTP1。0配置了connection:keepalive选项的服务器会保持一段时间的长连接。但标准库nethttp的连接默认只在服务器主动要求关闭时才断开,所以你的程序可能会消耗完socket描述符。解决办法有2个,请求结束后:直接设置请求变量的Close字段值为true,每次请求结束后就会主动关闭连接。设置Header请求头部选项Connection:close,然后服务器返回的响应头部也会有这个选项,此时HTTP标准库会主动断开连接。主动关闭连接funcmain(){req,err:http。NewRequest(GET,http:golang。org,nil)checkError(err)req。Closetruereq。Header。Add(Connection,close)等效的关闭方式resp,err:http。DefaultClient。Do(req)ifresp!nil{deferresp。Body。Close()}checkError(err)body,err:ioutil。ReadAll(resp。Body)checkError(err)fmt。Println(string(body))}
  你可以创建一个自定义配置的HTTPtransport客户端,用来取消HTTP全局的复用连接:funcmain(){tr:http。Transport{DisableKeepAlives:true}client:http。Client{Transport:tr}resp,err:client。Get(https:golang。google。cn)ifresp!nil{deferresp。Body。Close()}checkError(err)fmt。Println(resp。StatusCode)200body,err:ioutil。ReadAll(resp。Body)checkError(err)fmt。Println(len(string(body)))}
  根据需求选择使用场景:若你的程序要向同一服务器发大量请求,使用默认的保持长连接。若你的程序要连接大量的服务器,且每台服务器只请求一两次,那收到请求后直接关闭连接。或增加最大文件打开数fs。filemax的值。38。将JSON中的数字解码为interface类型
  在encodedecodeJSON数据时,Go默认会将数值当做float64处理,比如下边的代码会造成panic:funcmain(){vardata〔〕byte({status:200})varresultmap〔string〕interface{}iferr:json。Unmarshal(data,result);err!nil{log。Fatalln(err)}fmt。Printf(T,result〔status〕)float64varstatusresult〔status〕。(int)类型断言错误fmt。Println(Statusvalue:,status)}
  panic:interfaceconversion:interface{}isfloat64,notint
  如果你尝试decode的JSON字段是整型,你可以:将int值转为float统一使用将decode后需要的float值转为int使用将decode的值转为int使用funcmain(){vardata〔〕byte({status:200})varresultmap〔string〕interface{}iferr:json。Unmarshal(data,result);err!nil{log。Fatalln(err)}varstatusuint64(result〔status〕。(float64))fmt。Println(Statusvalue:,status)}使用Decoder类型来decodeJSON数据,明确表示字段的值类型指定字段类型funcmain(){vardata〔〕byte({status:200})varresultmap〔string〕interface{}vardecoderjson。NewDecoder(bytes。NewReader(data))decoder。UseNumber()iferr:decoder。Decode(result);err!nil{log。Fatalln(err)}varstatus,result〔status〕。(json。Number)。Int64()fmt。Println(Statusvalue:,status)}你可以使用string来存储数值数据,在decode时再决定按int还是float使用将数据转为decode为stringfuncmain(){vardata〔〕byte({status:200})varresultmap〔string〕interface{}vardecoderjson。NewDecoder(bytes。NewReader(data))decoder。UseNumber()iferr:decoder。Decode(result);err!nil{log。Fatalln(err)}varstatusuint64err:json。Unmarshal(〔〕byte(result〔status〕。(json。Number)。String()),status);checkError(err)fmt。Println(Statusvalue:,status)}
  使用struct类型将你需要的数据映射为数值型struct中指定字段类型funcmain(){vardata〔〕byte({status:200})varresultstruct{Statusuint64json:status}err:json。NewDecoder(bytes。NewReader(data))。Decode(result)checkError(err)fmt。Printf(Result:v,result)}可以使用struct将数值类型映射为json。RawMessage原生数据类型
  适用于如果JSON数据不着急decode或JSON某个字段的值类型不固定等情况:状态名称可能是int也可能是string,指定为json。RawMessage类型funcmain(){records:〔〕〔〕byte{〔〕byte({status:200,tag:one}),〔〕byte({status:ok,tag:two}),}foridx,record:rangerecords{varresultstruct{StatusCodeuint64StatusNamestringStatusjson。RawMessagejson:statusTagstringjson:tag}err:json。NewDecoder(bytes。NewReader(record))。Decode(result)checkError(err)varnamestringerrjson。Unmarshal(result。Status,name)iferrnil{result。StatusNamename}varcodeuint64errjson。Unmarshal(result。Status,code)iferrnil{result。StatusCodecode}fmt。Printf(〔v〕resultv,idx,result)}39。struct、array、slice和map的值比较
  可以使用相等运算符来比较结构体变量,前提是两个结构体的成员都是可比较的类型:typedatastruct{numintfpfloat32complexcomplex64strstringcharruneyesbooleventschanstringhandlerinterface{}refbyteraw〔10〕byte}funcmain(){v1:data{}v2:data{}fmt。Println(v1v2:,v1v2)true}
  如果两个结构体中有任意成员是不可比较的,将会造成编译错误。注意数组成员只有在数组元素可比较时候才可比较。typedatastruct{numintchecks〔10〕func()bool无法比较doItfunc()bool无法比较mmap〔string〕string无法比较bytes〔〕byte无法比较}funcmain(){v1:data{}v2:data{}fmt。Println(v1v2:,v1v2)}
  invalidoperation:v1v2(structcontaining〔10〕func()boolcannotbecompared)
  Go提供了一些库函数来比较那些无法使用比较的变量,比如使用reflect包的DeepEqual():比较相等运算符无法比较的元素funcmain(){v1:data{}v2:data{}fmt。Println(v1v2:,reflect。DeepEqual(v1,v2))truem1:map〔string〕string{one:a,two:b}m2:map〔string〕string{two:b,one:a}fmt。Println(v1v2:,reflect。DeepEqual(m1,m2))trues1:〔〕int{1,2,3}s2:〔〕int{1,2,3}注意两个slice相等,值和顺序必须一致fmt。Println(v1v2:,reflect。DeepEqual(s1,s2))true}
  这种比较方式可能比较慢,根据你的程序需求来使用。DeepEqual()还有其他用法:funcmain(){varb1〔〕bytenilb2:〔〕byte{}fmt。Println(b1b2:,reflect。DeepEqual(b1,b2))false}
  注意:DeepEqual()并不总适合于比较slicefuncmain(){varstronevarininterface{}onefmt。Println(strin:,reflect。DeepEqual(str,in))truev1:〔〕string{one,two}v2:〔〕string{two,one}fmt。Println(v1v2:,reflect。DeepEqual(v1,v2))falsedata:map〔string〕interface{}{code:200,value:〔〕string{one,two},}encoded,:json。Marshal(data)vardecodedmap〔string〕interface{}json。Unmarshal(encoded,decoded)fmt。Println(datadecoded:,reflect。DeepEqual(data,decoded))false}
  如果要大小写不敏感来比较byte或string中的英文文本,可以使用bytes或strings包的ToUpper()和ToLower()函数。比较其他语言的byte或string,应使用bytes。EqualFold()和strings。EqualFold()
  如果byteslice中含有验证用户身份的数据(密文哈希、token等),不应再使用reflect。DeepEqual()、bytes。Equal()、bytes。Compare()。这三个函数容易对程序造成timingattacks,此时应使用cryptosubtle包中的subtle。ConstantTimeCompare()等函数reflect。DeepEqual()认为空slice与nilslice并不相等,但注意byte。Equal()会认为二者相等:funcmain(){varb1〔〕bytenilb2:〔〕byte{}b1与b2长度相等、有相同的字节序nil与slice在字节上是相同的fmt。Println(b1b2:,bytes。Equal(b1,b2))true}40。从panic中恢复
  在一个defer延迟执行的函数中调用recover(),它便能捕捉中断panic错误的recover调用示例funcmain(){recover()什么都不会捕捉panic(notgood)发生panic,主程序退出recover()不会被执行println(ok)}正确的recover调用示例funcmain(){deferfunc(){fmt。Println(recovered:,recover())}()panic(notgood)}
  从上边可以看出,recover()仅在defer执行的函数中调用才会生效。错误的调用示例funcmain(){deferfunc(){doRecover()}()panic(notgood)}funcdoRecover(){fmt。Println(recobered:,recover())}
  recobered:panic:notgood41。在range迭代slice、array、map时通过更新引用来更新元素
  在range迭代中,得到的值其实是元素的一份值拷贝,更新拷贝并不会更改原来的元素,即是拷贝的地址并不是原有元素的地址:funcmain(){data:〔〕int{1,2,3}for,v:rangedata{v10data中原有元素是不会被修改的}fmt。Println(data:,data)data:〔123〕}
  如果要修改原有元素的值,应该使用索引直接访问:funcmain(){data:〔〕int{1,2,3}fori,v:rangedata{data〔i〕v10}fmt。Println(data:,data)data:〔102030〕}
  如果你的集合保存的是指向值的指针,需稍作修改。依旧需要使用索引访问元素,不过可以使用range出来的元素直接更新原有值:funcmain(){data:〔〕struct{numint}{{1},{2},{3},}for,v:rangedata{v。num10直接使用指针更新}fmt。Println(data〔0〕,data〔1〕,data〔2〕){10}{20}{30}}42。slice中隐藏的数据
  从slice中重新切出新slice时,新slice会引用原slice的底层数组。如果跳了这个坑,程序可能会分配大量的临时slice来指向原底层数组的部分数据,将导致难以预料的内存使用。funcget()〔〕byte{raw:make(〔〕byte,10000)fmt。Println(len(raw),cap(raw),raw〔0〕)10000100000xc420080000returnraw〔:3〕重新分配容量为10000的slice}funcmain(){data:get()fmt。Println(len(data),cap(data),data〔0〕)3100000xc420080000}
  可以通过拷贝临时slice的数据,而不是重新切片来解决:funcget()(res〔〕byte){raw:make(〔〕byte,10000)fmt。Println(len(raw),cap(raw),raw〔0〕)10000100000xc420080000resmake(〔〕byte,3)copy(res,raw〔:3〕)return}funcmain(){data:get()fmt。Println(len(data),cap(data),data〔0〕)330xc4200160b8}43。Slice中数据的误用
  举个简单例子,重写文件路径(存储在slice中)
  分割路径来指向每个不同级的目录,修改第一个目录名再重组子目录名,创建新路径:错误使用slice的拼接示例funcmain(){path:〔〕byte(AAAABBBBBBBBB)sepIndex:bytes。IndexByte(path,)4println(sepIndex)dir1:path〔:sepIndex〕dir2:path〔sepIndex1:〕println(dir1:,string(dir1))AAAAprintln(dir2:,string(dir2))BBBBBBBBBdir1append(dir1,suffix。。。)println(currentpath:,string(path))AAAAsuffixBBBBpathbytes。Join(〔〕〔〕byte{dir1,dir2},〔〕byte{})println(dir1:,string(dir1))AAAAsuffixprintln(dir2:,string(dir2))uffixBBBBprintln(newpath:,string(path))AAAAsuffixuffixBBBB错误结果}
  拼接的结果不是正确的AAAAsuffixBBBBBBBBB,因为dir1、dir2两个slice引用的数据都是path的底层数组,第13行修改dir1同时也修改了path,也导致了dir2的修改
  解决方法:重新分配新的slice并拷贝你需要的数据使用完整的slice表达式:input〔low:high:max〕,容量便调整为maxlow使用fullsliceexpressionfuncmain(){path:〔〕byte(AAAABBBBBBBBB)sepIndex:bytes。IndexByte(path,)4dir1:path〔:sepIndex:sepIndex〕此时cap(dir1)指定为4,而不是先前的16dir2:path〔sepIndex1:〕dir1append(dir1,suffix。。。)pathbytes。Join(〔〕〔〕byte{dir1,dir2},〔〕byte{})println(dir1:,string(dir1))AAAAsuffixprintln(dir2:,string(dir2))BBBBBBBBBprintln(newpath:,string(path))AAAAsuffixBBBBBBBBB}
  第6行中第三个参数是用来控制dir1的新容量,再往dir1中append超额元素时,将分配新的buffer来保存。而不是覆盖原来的path底层数组44。旧slice
  当你从一个已存在的slice创建新slice时,二者的数据指向相同的底层数组。如果你的程序使用这个特性,那需要注意旧(stale)slice问题。
  某些情况下,向一个slice中追加元素而它指向的底层数组容量不足时,将会重新分配一个新数组来存储数据。而其他slice还指向原来的旧底层数组。超过容量将重新分配数组来拷贝值、重新存储funcmain(){s1:〔〕int{1,2,3}fmt。Println(len(s1),cap(s1),s1)33〔123〕s2:s1〔1:〕fmt。Println(len(s2),cap(s2),s2)22〔23〕fori:ranges2{s2〔i〕20}此时的s1与s2是指向同一个底层数组的fmt。Println(s1)〔12223〕fmt。Println(s2)〔2223〕s2append(s2,4)向容量为2的s2中再追加元素,此时将分配新数组来存fori:ranges2{s2〔i〕10}fmt。Println(s1)〔12223〕此时的s1不再更新,为旧数据fmt。Println(s2)〔323314〕}45。类型声明与方法
  从一个现有的非interface类型创建新类型时,并不会继承原有的方法:定义Mutex的自定义类型typemyMutexsync。Mutexfuncmain(){varmtxmyMutexmtx。Lock()mtx。UnLock()}
  mtx。Lockundefined(typemyMutexhasnofieldormethodLock)
  如果你需要使用原类型的方法,可将原类型以匿名字段的形式嵌到你定义的新struct中:类型以字段形式直接嵌入typemyLockerstruct{sync。Mutex}funcmain(){varlockermyLockerlocker。Lock()locker。Unlock()}
  interface类型声明也保留它的方法集:typemyLockersync。Lockerfuncmain(){varlockermyLockerlocker。Lock()locker。Unlock()}46。跳出forswitch和forselect代码块
  没有指定标签的break只会跳出switchselect语句,若不能使用return语句跳出的话,可为break跳出标签指定的代码块:break配合label跳出指定代码块funcmain(){loop:for{switch{casetrue:fmt。Println(breakingout。。。)break死循环,一直打印breakingout。。。breakloop}}fmt。Println(out。。。)}
  goto虽然也能跳转到指定位置,但依旧会再次进入forswitch,死循环。47。for语句中的迭代变量与闭包函数
  for语句中的迭代变量在每次迭代中都会重用,即for中创建的闭包函数接收到的参数始终是同一个变量,在goroutine开始执行时都会得到同一个迭代值:funcmain(){data:〔〕string{one,two,three}for,v:rangedata{gofunc(){fmt。Println(v)}()}time。Sleep(3time。Second)输出threethreethree}
  最简单的解决方法:无需修改goroutine函数,在for内部使用局部变量保存迭代值,再传参:funcmain(){data:〔〕string{one,two,three}for,v:rangedata{vCopy:vgofunc(){fmt。Println(vCopy)}()}time。Sleep(3time。Second)输出onetwothree}
  另一个解决方法:直接将当前的迭代值以参数形式传递给匿名函数:funcmain(){data:〔〕string{one,two,three}for,v:rangedata{gofunc(instring){fmt。Println(in)}(v)}time。Sleep(3time。Second)输出onetwothree}
  注意下边这个稍复杂的3个示例区别:typefieldstruct{namestring}func(pfield)print(){fmt。Println(p。name)}错误示例funcmain(){data:〔〕field{{one},{two},{three}}for,v:rangedata{gov。print()}time。Sleep(3time。Second)输出threethreethree}正确示例funcmain(){data:〔〕field{{one},{two},{three}}for,v:rangedata{v:vgov。print()}time。Sleep(3time。Second)输出onetwothree}正确示例funcmain(){data:〔〕field{{one},{two},{three}}for,v:rangedata{此时迭代值v是三个元素值的地址,每次v指向的值不同gov。print()}time。Sleep(3time。Second)输出onetwothree}48。defer函数的参数值
  对defer延迟执行的函数,它的参数会在声明时候就会求出具体值,而不是在执行时才求值:在defer函数中参数会提前求值funcmain(){vari1deferfmt。Println(result:,func()int{returni2}())i}
  result:249。defer函数的执行时机
  对defer延迟执行的函数,会在调用它的函数结束时执行,而不是在调用它的语句块结束时执行,注意区分开。
  比如在一个长时间执行的函数里,内部for循环中使用defer来清理每次迭代产生的资源调用,就会出现问题:www。topgoer。comgo语言中文文档命令行参数指定目录名遍历读取目录下的文件funcmain(){iflen(os。Args)!2{os。Exit(1)}dir:os。Args〔1〕start,err:os。Stat(dir)iferr!nil!start。IsDir(){os。Exit(2)}vartargets〔〕stringfilepath。Walk(dir,func(fPathstring,fInfoos。FileInfo,errerror)error{iferr!nil{returnerr}if!fInfo。Mode()。IsRegular(){returnnil}targetsappend(targets,fPath)returnnil})for,target:rangetargets{f,err:os。Open(target)iferr!nil{fmt。Println(badtarget:,target,error:,err)error:toomanyopenfilesbreak}deferf。Close()在每次for语句块结束时,不会关闭文件资源使用f资源}}
  先创建10000个文件:!binbashfornin{1。。10000};doechocontentfile{n}。txtdone
  运行效果:
  解决办法:defer延迟执行的函数写入匿名函数中:目录遍历正常funcmain(){。。。for,target:rangetargets{func(){f,err:os。Open(target)iferr!nil{fmt。Println(badtarget:,target,error:,err)return在匿名函数内使用return代替break即可}deferf。Close()匿名函数执行结束,调用关闭文件资源使用f资源}()}}
  当然你也可以去掉defer,在文件资源使用完毕后,直接调用f。Close()来关闭。50。失败的类型断言
  在类型断言语句中,断言失败则会返回目标类型的零值,断言变量与原来变量混用可能出现异常情况:错误示例funcmain(){vardatainterface{}greatdata混用ifdata,ok:data。(int);ok{fmt。Println(〔isanint〕,data:,data)}else{fmt。Println(〔notanint〕,data:,data)〔isntaint〕,data:0}}正确示例funcmain(){vardatainterface{}greatifres,ok:data。(int);ok{fmt。Println(〔isanint〕,data:,res)}else{fmt。Println(〔notanint〕,data:,data)〔notanint〕,data:great}}51。阻塞的gorutinue与资源泄露
  在2012年GoogleIO大会上,RobPike的GoConcurrencyPatterns演讲讨论Go的几种基本并发模式,如完整代码中从数据集中获取第一条数据的函数:funcFirst(querystring,replicas〔〕Search)Result{c:make(chanResult)replicaSearch:func(iint){creplicas〔i〕(query)}fori:rangereplicas{goreplicaSearch(i)}returnc}
  在搜索重复时依旧每次都起一个goroutine去处理,每个goroutine都把它的搜索结果发送到结果channel中,channel中收到的第一条数据会直接返回。
  返回完第一条数据后,其他goroutine的搜索结果怎么处理?他们自己的协程如何处理?
  在First()中的结果channel是无缓冲的,这意味着只有第一个goroutine能返回,由于没有receiver,其他的goroutine会在发送上一直阻塞。如果你大量调用,则可能造成资源泄露。
  为避免泄露,你应该确保所有的goroutine都能正确退出,有2个解决方法:使用带缓冲的channel,确保能接收全部goroutine的返回结果:funcFirst(querystring,replicas。。。Search)Result{c:make(chanResult,len(replicas))searchReplica:func(iint){creplicas〔i〕(query)}fori:rangereplicas{gosearchReplica(i)}returnc}使用select语句,配合能保存一个缓冲值的channeldefault语句:
  default的缓冲channel保证了即使结果channel收不到数据,也不会阻塞goroutinefuncFirst(querystring,replicas。。。Search)Result{c:make(chanResult,1)searchReplica:func(iint){select{casecreplicas〔i〕(query):default:}}fori:rangereplicas{gosearchReplica(i)}returnc}使用特殊的废弃(cancellation)channel来中断剩余goroutine的执行:funcFirst(querystring,replicas。。。Search)Result{c:make(chanResult)done:make(chanstruct{})deferclose(done)searchReplica:func(iint){select{casecreplicas〔i〕(query):casedone:}}fori:rangereplicas{gosearchReplica(i)}returnc}
  RobPike为了简化演示,没有提及演讲代码中存在的这些问题。不过对于新手来说,可能会不加思考直接使用。

年初订单已同比增20!为实现一季度开门红,沪郊企业生产线马力全开目前员工返岗率超过95,今年初的订单也比去年同期增长了20。开工的这几天,大家都干劲十足,对企业发展充满了信心眼下,在沪郊金山卫镇,上海宏晨家庭用品有限公司正在全力备战2000万元不会吧?出境还去当地找换店换汇?坐标深圳,作为疫情前年年出境旅行和没事香港逛的老鸟,同大家分享一个境外取现免手续费的银行卡上海银行借记卡此卡是几年前申请的,19年在哈萨克斯坦和土耳其亲测过取现无发卡行手续费,疫情德馨食品冲刺深交所年营收5。3亿林志勇控制75股权雷递网雷建平2月13日浙江德馨食品科技股份有限公司(简称德馨食品)日前预更新招股书,准备在深交所主板上市。德馨食品计划募资9亿元。其中,3。96亿元用于年产3。8万吨饮品配料生产基曲德君失联,新城控股将重回父子掌权时代?2月10日晚间,新城控股发布公告称,公司暂时无法与公司董事联席总裁曲德君取得联系。截至目前,公司尚未能了解到具体原因。公司经营决策机制健全管理规范,前述事项不会对公司经营活动产生影再出海!石药集团订立一项海外独家授权2月13日,石药集团公告,集团附属公司石药集团巨石生物制药有限公司(下称石药巨石生物)已与CorbusPharmaceuticals,Inc。(下称CorbusPharmaceut万亿咖啡市场行业从不寂寞库迪会是第二个咖啡陪你吗?2023年,对于从未平静的中国咖啡市场来说,注定又是特别的一年!2月6日,横空出世的库迪咖啡(COTTICOFFEE)宣布开启百城千店咖啡狂欢节。2月6日至3月31日,六大系列70我国富裕水平10强城市杭州第3,珠海力压苏州,南京成都无缘我国富裕水平10强城市根据2021年人均居民消费的多少,依次为上海(48879元)深圳(46286元)杭州(44609元)广州(44310元)北京(43640元)珠海(42334元巴基斯坦濒临破产,马六甲交通咽喉瓜达尔港,中国能否保住?巴基斯坦濒临破产,西方蠢蠢欲动,但瓜达尔港谁也惦记不了巴基斯坦正在破产边缘徘徊,或将成为下一个斯里兰卡。去年,老美一顿骚操作疯狂加息,而巴基斯坦很多东西都要依赖进口,也就使得外汇进湖屯镇起跑即冲刺,确保一季度开门红春节过后,肥城市湖屯镇各企业已经陆续开工,组织工人紧张有序地返岗复产。抓订单拓销路忙生产,企业撸起袖子赶进度,掀起新一轮生产热潮,确保实现2023年第一季度开门红。泰安鑫佳机械有限2022家居风云榜践行顾客第一打造服务标杆红星美凯龙用诚信服务持续为消费者创造美好生活前言沉舟侧畔千帆过,病树前头万木春。2022年,重庆家居行业在经历了疫情高温市场低迷等不利因素轮番冲击后,给本就竞争激烈的市场带来了更多的不确定性。面对困局,有的企业茫然无措信心丧内存优化基础论初识Android内存优化小木箱成长营内存优化系列文章内存优化工具论常见的Android内存优化工具和框架内存优化方法论揭开内存优化神秘面纱内存优化实战论内存优化实践与应用Tips关注微信公众号小木箱成长营
玩阴阳师怎样能比较快速的把式神升到六星,大佬们能给个建议吗?玩阴阳师怎样能比较快速的把式神升到六星,大佬们能给个建议吗?谢谢邀请。大家好,我是柠檬酸酸鼠,一个喜欢玩阴阳师的柠檬。式神升到六星,这里面确实有些技巧呢。从5星升级到6星,需要5个教师几月份可能会涨工资?为什么?具体几月份涨工资还没有具体的实施细则,但在2017年9月24日中共中央办公厅国务院办公厅印发关于深化教育体制机制改革的意见,中央提出提高教师待遇,确保平均工资水平不低于或高于当地公儿子去世,儿媳私自堕胎违法么?女人有生育权,丈夫去世后自己的选择不违法,公婆情感上无法接受,但是法理没问题不违法,0岁以下的胎儿还不能称作是法律上的人,孕妇有自主权利决定胎儿的去留,别说现在你儿子去世了,就是你魔兽世界怀旧服战士黑龙头没换项链的影响究竟有多大,真的需要删号重练吗?虽然魔兽世界怀旧服已经开到了3。5阶段(阿拉希战场已开放),但是直到目前依然有很多战士玩家在为了奥妮克希亚龙头兑换的奖励而纠结,在一些强力党的宣扬下,甚至出现了战士龙头换饰品角色直小鲨鱼也顶不住了!公开宣称为山东打球是工作而已,精气神何在?几乎每场40出场时间谁也受不了更何况这只是常规赛很多比赛甚至大比分落后徐长锁也不知道轮换很显然这样的情况下谁也受不了,陶汉林虽然被称为小鲨鱼但是也不是铁人也需要休息,也需要队友分担数字电源为什么一般用DSP控制,而不能用普通的单片机?普通单片机无法做到高速AD采样,无法做到高频率高精度PWM相位控制,无法做到高速计算,这些恰恰是数字电源需要的,不过目前好多32位Arm也集成了这些功能,相当于有了部分DSP功能,东京奥运会之后,李盈莹张常宁袁心玥和王梦洁都有可能成为世界级球员,你怎么看?凭啥说李盈莹张常宁袁心玥和王梦洁在东京奥运后会成为世界级球员?到底啥样的球员叫做世界级球员?参加过奥运会的就叫世界级球员吗?张常宁和袁心玥都参加过奥运,朱袁张朱袁张,不是已经叫了三膝盖响是怎么回事?膝盖响有挺多原因的,一般最常见的原因就是滑膜炎,还有就是半月板损伤,还有一种可能就是膝关节里边有骨质增生,最常见的也就是这几种原因,建议还是做一个X线或者是MRI,下面为大家介绍一北京冬奥会开幕式,会不会像2008年北京奥运会一样震惊世界?北京冬奥会开幕式!冬天给全世界带来暖冬微笑!欢迎各国体育健儿!欢迎所有友好国家人民,先生女土观临!伟大的北京是祖国心脏!世界人民向往的地方!北京加油!绿色冬奥会号角吹响!2022虎江苏女排为何没有复制天津女排的奇迹?问题出在哪里?第一,格局小,沉迷于全苏班不能自拔。第二,训练量不够,娇骄二气。第三,浓装艳抹,热衷于广告代言。第四,不正面差距,以各种理由为输球找借口。第五,球迷被不良自媒体误导!排球环境极差。颜妮发文道出未参赛原因,她退役后中国女排副攻线怎么办?颜妮因为肩伤加重,而提前退出了全运会的比赛。辽宁女排在获得铜牌之后,丁霞等人也把颜妮的照片一起带上了领奖台,颜妮也为此发文表示感谢。那么中国女排在徐云丽颜妮南北长城双双退役之后,中
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网