go启动的参数奥妙

更多好文请v
golang技术实验室

1、GOGC

GOGC 用于控制GC的处发频率, 其值默认为100,

意为直到自上次垃圾回收后heap size已经增长了100%时GC才触发运行。即是GOGC=100意味着live heap size 每增长一倍,GC触发运行一次。

如设定GOGC=200, 则live heap size 自上次垃圾回收后,增长2倍时,GC触发运行, 总之,其值越大则GC触发运行频率越低, 反之则越高,

如果GOGC=off 则关闭GC.

/**
 * @Author: zHangSan
 * @Description:
 * @File:  main
 * @Version: 1.0.0
 * @Date: 2021/4/16 下午4:44
 */

package main

import "runtime"

func main(){
	gc()
	runtime.GC()
	gc()
	gc()
	gc()

}

func gc(){
	for i := 0; i< 10000;i++{
		g := make(map[string]string)
		g = g
	}
}
GOGC=100 GODEBUG=gctrace=1 go run ./main.go
gc 1 @0.017s 0%: 0.008+0.48+0.004 ms clock, 0.032+0.33/0.22/0.33+0.019 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 2 @0.031s 0%: 0.003+0.33+0.002 ms clock, 0.014+0.27/0.17/0.57+0.010 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 3 @0.057s 0%: 0.012+0.35+0.003 ms clock, 0.050+0/0.27/0.57+0.012 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
# command-line-arguments
gc 1 @0.002s 9%: 0.023+3.1+0.036 ms clock, 0.093+0.20/2.0/4.1+0.14 ms cpu, 4->7->6 MB, 5 MB goal, 4 P
gc 2 @0.015s 8%: 0.004+4.0+0.003 ms clock, 0.017+1.4/2.8/2.0+0.013 ms cpu, 10->13->13 MB, 12 MB goal, 4 P
gc 1 @0.000s 3%: 0.008+0.13+0.002 ms clock, 0.032+0/0.045/0.16+0.008 ms cpu, 0->0->0 MB, 4 MB goal, 4 P (forced)

可以看到第二次的GC的时候是上次的两倍

2、GOTRACEBACK 打印异常信息

GOTRACEBACK用于控制当异常发生时,系统提供信息的详细程度, 在go 1.5, GOTRACEBACK有4个值。

GOTRACEBACK=0 只输出panic异常信息。
GOTRACEBACK=1 此为go的默认设置值, 输出所有goroutine的stack traces, 除去与go runtime相关的stack frames.
GOTRACEBACK=2 在GOTRACEBACK=1的基础上, 还输出与go runtime相关的stack frames,从而了解哪些goroutines是由go runtime启动运行的。
GOTRACEBACK=crash, 在GOTRACEBACK=2的基础上,go runtime处发进程segfault错误,从而生成core dump, 当然要操作系统允许的情况下, 而不是调用os.Exit。

package main

import "runtime"

func main(){
	//goGC()

	gOTraceBack()

}
/*****************************GOTRACEBACK-异常发生打印详细信息****************************************/
func gOTraceBack(){
	panic("kerboom")

}
GOTRACEBACK=2 go run main.go
-> % GOTRACEBACK=2 go run main.go               
panic: kerboom

goroutine 1 [running]:
panic(0x105ff60, 0x10839b0)
        /usr/local/go/src/runtime/panic.go:1064 +0x46d fp=0xc000030768 sp=0xc0000306b0 pc=0x1028a2d
main.gOTraceBack(...)
        /Users/zhangsan/Documents/GitHub/testCode/runArgs/main.go:21
main.main()
        /Users/zhangsan/Documents/GitHub/testCode/runArgs/main.go:16 +0x39 fp=0xc000030788 sp=0xc000030768 pc=0x1056e89
runtime.main()
        /usr/local/go/src/runtime/proc.go:203 +0x212 fp=0xc0000307e0 sp=0xc000030788 pc=0x102b452
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc0000307e8 sp=0xc0000307e0 pc=0x10529d1

goroutine 2 [force gc (idle)]:
runtime.gopark(0x1077928, 0x10cd3a0, 0x1411, 0x1)
        /usr/local/go/src/runtime/proc.go:304 +0xe0 fp=0xc000030fb0 sp=0xc000030f90 pc=0x102b820
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:310
runtime.forcegchelper()
        /usr/local/go/src/runtime/proc.go:253 +0xb7 fp=0xc000030fe0 sp=0xc000030fb0 pc=0x102b6d7
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc000030fe8 sp=0xc000030fe0 pc=0x10529d1
created by runtime.init.6
        /usr/local/go/src/runtime/proc.go:242 +0x35

goroutine 3 [GC sweep wait]:
runtime.gopark(0x1077928, 0x10cd460, 0x140c, 0x1)
        /usr/local/go/src/runtime/proc.go:304 +0xe0 fp=0xc0000317a8 sp=0xc000031788 pc=0x102b820
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:310
runtime.bgsweep(0xc00001c070)
        /usr/local/go/src/runtime/mgcsweep.go:70 +0x9c fp=0xc0000317d8 sp=0xc0000317a8 pc=0x101aa7c
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc0000317e0 sp=0xc0000317d8 pc=0x10529d1
created by runtime.gcenable
        /usr/local/go/src/runtime/mgc.go:214 +0x5c

goroutine 4 [GC scavenge wait]:
runtime.gopark(0x1077928, 0x10cd420, 0x140d, 0x1)
        /usr/local/go/src/runtime/proc.go:304 +0xe0 fp=0xc000031f78 sp=0xc000031f58 pc=0x102b820
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:310
runtime.bgscavenge(0xc00001c070)
        /usr/local/go/src/runtime/mgcscavenge.go:237 +0xd0 fp=0xc000031fd8 sp=0xc000031f78 pc=0x1018fd0
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc000031fe0 sp=0xc000031fd8 pc=0x10529d1
created by runtime.gcenable
        /usr/local/go/src/runtime/mgc.go:215 +0x7e
exit status 2

GOTRACEBACK 在go 1.6中的变化

GOTRACEBACK=none 只输出panic异常信息。
GOTRACEBACK=single 只输出被认为引发panic异常的那个goroutine的相关信息。
GOTRACEBACK=all 输出所有goroutines的相关信息,除去与go runtime相关的stack frames.
GOTRACEBACK=system 输出所有goroutines的相关信息,包括与go runtime相关的stack frames,从而得知哪些goroutine是go runtime启动运行的。
GOTRACEBACK=crash 与go 1.5相同, 未变化。

为了与go 1.5兼容,0 对应 none, 1 对应 all, 以及 2 对应 system.

注意: 在go 1.6中, 默认,只输出引发panci异常的goroutine的stack trace.

3、控制CPU数量GOMAXPROCS

GOMAXPROCS

GOMAXPROCS 大家比较熟悉, 用于控制操作系统的线程数量, 这些线程用于运行go程序中的goroutines.
到go 1.5的时候, GOMAXPROCS的默认值就是我们的go程序启动时可见的操作系统认为的CPU个数。

注意: 在我们的go程序中使用的操作系统线程数量,也包括:正服务于cgo calls的线程, 阻塞于操作系统calls的线程,
所以go 程序中使用的操作系统线程数量可能大于GOMAXPROCS的值。

4、GODEBUG

GODEBUG=gctrace=1,schedtrace=1000 godoc -http=:8080

1、gctrace

gctrace用途主要是用于跟踪GC的不同阶段的耗时与GC前后的内存量对比。

package main

import "runtime"

func main(){
	//goGC()

	//gOTraceBack() 查看crash的时候相信信息

	goDebugGc()

}
/*****************************GODEBUG-异常发生打印详细信息****************************************/
func goDebugGc(){
	gc()
}
func gc(){
	for i := 0; i< 10000;i++{
		g := make(map[string]string)
		g = g
	}
}
GODEBUG=gctrace=1 go run  main.go                            
gc 1 @0.109s 0%: 0.023+0.68+0.005 ms clock, 0.094+0.66/0.44/0+0.021 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 2 @0.136s 0%: 0.004+0.37+0.003 ms clock, 0.019+0.29/0.16/0.68+0.012 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 3 @0.283s 0%: 0.020+0.47+0.003 ms clock, 0.081+0/0.31/0.66+0.014 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
# command-line-arguments
gc 1 @0.019s 6%: 0.057+8.1+0.086 ms clock, 0.23+1.5/5.0/10+0.34 ms cpu, 4->4->3 MB, 5 MB goal, 4 P
gc 4 @0.407s 0%: 0.010+1.0+0.059 ms clock, 0.043+0.34/0.18/0.60+0.23 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
# command-line-arguments
gc 1 @0.002s 12%: 0.007+3.8+0.038 ms clock, 0.029+0.17/2.8/1.8+0.15 ms cpu, 4->6->6 MB, 5 MB goal, 4 P
gc 2 @0.021s 9%: 0.005+8.8+0.003 ms clock, 0.020+0/8.0/1.5+0.014 ms cpu, 9->13->13 MB, 12 MB goal, 4 P
  1. gc 1 : 代表第一次gc

  2. @0.109 : 这是gc的markTermination阶段完成,距离runtime到现在 STW的最后工作,关闭内存屏障,停止后台标记及辅助标记,做一些清理工作

  3. 0% : 当前为止,gc的标记工作(包括两次mark阶段和STW和并发标记)多占用的CPU时间占PU的百分比

  4. 0.023+0.68+0.005 ms clock : 三部分 0。023表示整个过程在mar阶段STW停顿时间(单个P的);
    0.68表示并发标记时间(所有P的);0.005表示markTermination的STW时间(单个P)的

  5. 0.094+0.66/0.44/0+0.021 ms cpu : 0.094三部分,0.094表示整个进程在mark阶段STW停顿时间(0.023 * 8);
    0.66/0.44/0 三块信息,0.66是mutator assists占用的时间,0。44是dedicated mark worker+ fractional mark worker占用的时间,0是idle mark workers占用的时间,这些时间接近
    0.68 * P的个数,+0。021ms 表示整个进程在markTermination阶段Ste停顿时间0。005 * P的个数

  6. 4->4->0 MB 4表示开始mark阶段前的heap_live大小;4表示markTermination阶段之前的heap_live大小,0表示被标记对象的大小

  7. 5MB goal: 表示下一次触发GC的内存占用阀值是5MB,向上取整

  8. 4P :本次gc共有多少P

  • heap_live要结合go的内存管理来理解。因为go按照不同的对象大小,会分配不同页数的span。span是对内存页进行管理的基本单元,每页8k大小。所以肯定会出现span中有内存是空闲着没被用上的。

不过怎么用go先不管,反正是把它划分给程序用了。而heap_live就表示所有span的大小。

而程序到底用了多少呢?就是在GC扫描对象时,扫描到的存活对象大小就是已用的大小。对应上面就是8MB。

  • mark worker分为三种,dedicated、fractional和idle。分别表示标记工作干活时的专注程度。dedicated最专注,除非被抢占打断,否则一直干活。idle最偷懒,干一点活就退出,控制权让给出别的goroutine。它们都是并发标记工作里的worker。

2、schedtrace 调度跟踪

GODEBUG=schedtrace=1000  go run  main.go 
SCHED 0ms: gomaxprocs=4 idleprocs=2 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 [0 0 0 0]
# command-line-arguments
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 [1 0 0 0]
# command-line-arguments
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=5 spinningthreads=1 idlethreads=0 runqueue=0 [0 0 0 0]
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 [1 0 0 0]

可以使运行时在每 X 毫秒发出一次详细的多行信息,信息内容主要包括调度程序、处理器、OS 线程 和 Goroutine 的状态。

sched:每一行都代表调度器的调试信息,后面提示的毫秒数表示启动到现在的运行时间,输出的时间间隔受 schedtrace 的值影响。

gomaxprocs:当前的 CPU 核心数(GOMAXPROCS 的当前值)。

idleprocs:空闲的处理器数量,后面的数字表示当前的空闲数量。

threads:OS 线程数量,后面的数字表示当前正在运行的线程数量。

spinningthreads:自旋状态的 OS 线程数量。

idlethreads:空闲的线程数量。

runqueue:全局队列中中的 Goroutine 数量,而后面的 [0 0 1 1] 则分别代表这 4 个 P 的本地队列正在运行的 Goroutine 数量。

详细信息

GODEBUG=scheddetail=1,schedtrace=1000  go run  main.go

SCHED 0ms: gomaxprocs=4 idleprocs=2 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0
  P0: status=1 schedtick=0 syscalltick=0 m=0 runqsize=0 gfreecnt=0 timerslen=0
  P1: status=1 schedtick=0 syscalltick=0 m=3 runqsize=0 gfreecnt=0 timerslen=0
  P2: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
  P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
  M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M1: p=-1 curg=17 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=17
  M0: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=1
  G1: status=1() m=-1 lockedm=0
  G17: status=6() m=1 lockedm=1
  G2: status=1() m=-1 lockedm=-1
# command-line-arguments
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 [1 0 0 0]
# command-line-arguments
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=5 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=1 stopwait=0 sysmonwait=0
  P0: status=1 schedtick=0 syscalltick=0 m=3 runqsize=0 gfreecnt=0 timerslen=0
  P1: status=1 schedtick=1 syscalltick=0 m=2 runqsize=0 gfreecnt=0 timerslen=0
  P2: status=1 schedtick=0 syscalltick=0 m=4 runqsize=0 gfreecnt=0 timerslen=0
  P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
  M4: p=2 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M3: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M2: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=true blocked=false lockedg=-1
  M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=1
  G1: status=1(chan receive) m=-1 lockedm=0
  G2: status=4(force gc (idle)) m=-1 lockedm=-1
  G3: status=1() m=-1 lockedm=-1
  G4: status=4(GC scavenge wait) m=-1 lockedm=-1
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=5 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=1 stopwait=0 sysmonwait=0
  P0: status=1 schedtick=0 syscalltick=0 m=3 runqsize=0 gfreecnt=0 timerslen=0
  P1: status=1 schedtick=2 syscalltick=0 m=2 runqsize=0 gfreecnt=0 timerslen=0
  P2: status=1 schedtick=0 syscalltick=0 m=4 runqsize=0 gfreecnt=0 timerslen=0
  P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
  M4: p=2 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=true blocked=false lockedg=-1
  M3: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=true blocked=false lockedg=-1
  M2: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
  M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=1
  G1: status=1(chan receive) m=-1 lockedm=0
  G2: status=4(force gc (idle)) m=-1 lockedm=-1
  G3: status=4(GC sweep wait) m=-1 lockedm=-1
  G4: status=4(GC scavenge wait) m=-1 lockedm=-1

status:G 的运行状态。

m:隶属哪一个 M。

lockedm:是否有锁定 M。

在第一点中我们有提到 G 的运行状态,这对于分析内部流转非常的有用,共涉及如下 9 种状态:

状态 值 含义
_Gidle 0 刚刚被分配,还没有进行初始化。
_Grunnable 1 已经在运行队列中,还没有执行用户代码。
_Grunning 2 不在运行队列里中,已经可以执行用户代码,此时已经分配了 M 和 P。
_Gsyscall 3 正在执行系统调用,此时分配了 M。
_Gwaiting 4 在运行时被阻止,没有执行用户代码,也不在运行队列中,此时它正在某处阻塞等待中。
_Gmoribund_unused 5 尚未使用,但是在 gdb 中进行了硬编码。
_Gdead 6 尚未使用,这个状态可能是刚退出或是刚被初始化,此时它并没有执行用户代码,有可能有也有可能没有分配堆栈。
_Genqueue_unused 7 尚未使用。
_Gcopystack 8 正在复制堆栈,并没有执行用户代码,也不在运行队列中。
在理解了各类的状态的意思后,我们结合上述案例看看,如下:

G1: status=4(semacquire) m=-1 lockedm=-1
G2: status=4(force gc (idle)) m=-1 lockedm=-1
G3: status=4(GC sweep wait) m=-1 lockedm=-1
G17: status=1() m=-1 lockedm=-1
G18: status=2() m=4 lockedm=-1
在这个片段中,G1 的运行状态为 _Gwaiting,并没有分配 M 和锁定。这时候你可能好奇在片段中括号里的是什么东西呢,其实是因为该 status=4 是表示 Goroutine 在运行时时被阻止,而阻止它的事件就是 semacquire 事件,是因为 semacquire 会检查信号量的情况,在合适的时机就调用 goparkunlock 函数,把当前 Goroutine 放进等待队列,并把它设为 _Gwaiting 状态。

那么在实际运行中还有什么原因会导致这种现象呢,我们一起看看,如下:

waitReasonZero                                    // ""
waitReasonGCAssistMarking                         // "GC assist marking"
waitReasonIOWait                                  // "IO wait"
waitReasonChanReceiveNilChan                      // "chan receive (nil chan)"
waitReasonChanSendNilChan                         // "chan send (nil chan)"
waitReasonDumpingHeap                             // "dumping heap"
waitReasonGarbageCollection                       // "garbage collection"
waitReasonGarbageCollectionScan                   // "garbage collection scan"
waitReasonPanicWait                               // "panicwait"
waitReasonSelect                                  // "select"
waitReasonSelectNoCases                           // "select (no cases)"
waitReasonGCAssistWait                            // "GC assist wait"
waitReasonGCSweepWait                             // "GC sweep wait"
waitReasonChanReceive                             // "chan receive"
waitReasonChanSend                                // "chan send"
waitReasonFinalizerWait                           // "finalizer wait"
waitReasonForceGGIdle                             // "force gc (idle)"
waitReasonSemacquire                              // "semacquire"
waitReasonSleep                                   // "sleep"
waitReasonSyncCondWait                            // "sync.Cond.Wait"
waitReasonTimerGoroutineIdle                      // "timer goroutine (idle)"
waitReasonTraceReaderBlocked                      // "trace reader (blocked)"
waitReasonWaitForGCCycle                          // "wait for GC cycle"
waitReasonGCWorkerIdle                            // "GC worker (idle)"

我们通过以上 waitReason 可以了解到 Goroutine 会被暂停运行的原因要素,也就是会出现在括号中的事件。

M
p:隶属哪一个 P。

curg:当前正在使用哪个 G。

runqsize:运行队列中的 G 数量。

gfreecnt:可用的G(状态为 Gdead)。

mallocing:是否正在分配内存。

throwing:是否抛出异常。

preemptoff:不等于空字符串的话,保持 curg 在这个 m 上运行。

P
status:P 的运行状态。

schedtick:P 的调度次数。

syscalltick:P 的系统调用次数。

m:隶属哪一个 M。

runqsize:运行队列中的 G 数量。

gfreecnt:可用的G(状态为 Gdead)。

状态 值 含义
_Pidle 0 刚刚被分配,还没有进行进行初始化。
_Prunning 1 当 M 与 P 绑定调用 acquirep 时,P 的状态会改变为 _Prunning。
_Psyscall 2 正在执行系统调用。
_Pgcstop 3 暂停运行,此时系统正在进行 GC,直至 GC 结束后才会转变到下一个状态阶段。
_Pdead 4 废弃,不再使用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
九宫格问题是蓝桥杯中经典的编程题目之一,其奥妙在于如何通过编程算法来填充九宫格使得每行、每列以及对角线上的数字之和都相等。以下是一种解法: 首先,我们可以使用一个二维数组来表示九宫格,初始化为全0。然后,我们可以选定一个数字作为起始数字,将其放置在九宫格的中间位置(第二行、第二列)。接下来,我们按照如下的规则来填充九宫格: 1. 将当前数字放置在当前位置。 2. 如果当前位置已经到达右上角(第一行最后一列),则将下一个数字放置在左下角(最后一行第一列),即行号加1,列号减1。 3. 如果当前位置的行号超过了最后一行,则将下一个数字放置在当前位置的下方相同列的位置。 4. 如果当前位置的列号超过了最后一列,则将下一个数字放置在当前位置的右方相同行的位置。 5. 如果当前位置已经被占据,则将下一个数字放置在当前位置的下方相同列的位置。 通过按照以上规则不断填充数字,直到九宫格被填满为止。最后,我们可以输出九宫格的结果。 以下是一个示例代码: ```python def fillMagicSquare(n): # 创建一个n x n的二维数组并初始化为全0 magic_square = [[0 for _ in range(n)] for _ in range(n)] # 选定起始数字,并将其放置在九宫格的中间位置 num = 1 i = n // 2 j = n - 1 while num <= n ** 2: if i == -1 and j == n: i = 0 j = n - 2 else: if j == n: j = 0 if i < 0: i = n - 1 if magic_square[int(i)][int(j)]: # 检查当前位置是否已被占据 i = i + 1 j = j - 2 continue else: magic_square[int(i)][int(j)] = num num = num + 1 i = i - 1 j = j + 1 return magic_square # 测试代码 n = 3 magic_square = fillMagicSquare(n) for i in range(n): for j in range(n): print(magic_square[i][j], end=' ') print() ``` 以上代码可以生成一个3x3的九宫格,其每行、每列以及对角线上的数字之和都为15。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a...Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值